Error handling
Every failure the SDK raises is a TtsError carrying a machine-readable code. Catch broadly, then branch on the code.
import { TtsError, isTtsError } from "@optimalai/react-native";
try {
await tts.synthesizeToFile({ text: "你好。" });
} catch (error) {
if (isTtsError(error)) {
console.warn(error.code, error.message, error.details);
} else {
throw error;
}
}Prefer isTtsError over instanceof — it survives a duplicated module instance in the bundle.
The error codes
| Code | Meaning | Recoverable? |
|---|---|---|
MODEL_NOT_FOUND | A model asset could not be resolved. | Fatal — needs reset(). |
MODEL_CORRUPT | An asset exists but failed validation. | Fatal — needs reset(). |
UNSUPPORTED_DEVICE | The device or OS is below the baseline. | Fatal — needs reset(). |
BUSY | Another load or synthesis is already running. | Yes — retry after the current one finishes. |
INVALID_TEXT | The text contains an out-of-vocabulary word or unsupported character. | Yes — change the text. |
TEXT_TOO_LONG | Input exceeds 2,000 code points. | Yes — split the text. |
TEXT_SEGMENT_TOO_LONG | A segment cannot be split below the 300-code-point cap. | Yes — split the text. |
INFERENCE_FAILED | The ONNX session failed while generating audio. | Usually — retry, or reset() if it repeats. |
OUTPUT_EXISTS | outputPath already points at a file. | Yes — delete the file or use another path. |
OUTPUT_WRITE_FAILED | The WAV could not be written to disk. | Yes — check permissions and free space. |
Fatal codes need a reset
MODEL_NOT_FOUND, MODEL_CORRUPT, and UNSUPPORTED_DEVICE move the client to failed. From failed you cannot call load() directly — you must reset() first:
if (tts.status() === "failed") {
await tts.reset();
}
await tts.load();One synthesis at a time
A client runs a single synthesis at a time. Starting another while one is in flight throws BUSY:
// This will throw BUSY.
await Promise.all([
tts.synthesizeToFile({ text: "第一句。" }),
tts.synthesizeToFile({ text: "第二句。" }),
]);Serialize instead:
for (const text of ["第一句。", "第二句。"]) {
await tts.synthesizeToFile({ text });
}The same applies to load(): calling it while the client is loading or synthesizing throws BUSY. If you need parallelism, create a second client — but be aware each one opens its own ONNX session and holds its own memory.
Out-of-vocabulary text
English words not present in the bundled lexicon fail with INVALID_TEXT. There is no eSpeak fallback in the Beta — this is deliberate, to keep GPL code out of the dependency graph.
Chinese characters missing from the Chinese lexicon fail the same way.
try {
await tts.synthesizeToFile({ text: "Supercalifragilistic" });
} catch (error) {
if (isTtsError(error) && error.code === "INVALID_TEXT") {
// Show the user a "this word isn't supported" message.
}
}Validate or sanitize user-supplied text before you synthesize it, rather than letting a stray token disrupt a batch.
Surfacing errors in UI
A small helper keeps the code visible without leaking stack traces:
function errorMessage(error: unknown): string {
if (isTtsError(error)) return `${error.code}: ${error.message}`;
return error instanceof Error ? error.message : "Unknown error";
}The TtsError also carries an optional details record with extra context for diagnostics — log it, don't show it to users.