SubtitleEdit/subtitleedit · error · FileNotFoundException

OmniVoice TTS voice cloning requires a transcript file at {r

Error message

OmniVoice TTS voice cloning requires a transcript file at {refTextPath}. Re-import the voice to provide its transcript.

What it means

FileNotFoundException thrown when a cloned voice (omniVoice.FilePath points to an existing WAV) is selected but the sibling .txt transcript file (same basename, .txt extension) does not exist. omnivoice-tts requires --ref-text whenever --ref-wav is set, so SE fails loudly rather than silently producing the default speaker voice.

Source

Thrown at src/ui/Features/Video/TextToSpeech/Engines/OmniVoiceTtsCpp.cs:296

        // auto-detect, not "language not found".
        if (language != null && !string.IsNullOrEmpty(language.Code))
        {
            psi.ArgumentList.Add("--lang");
            psi.ArgumentList.Add(language.Code);
        }

        // Voice cloning: omnivoice-tts requires both --ref-wav and --ref-text.
        // Pair each voices/<name>.wav with a sibling <name>.txt holding the transcript.
        // The transcript is captured at import time; if it has been deleted out from under
        // us we fail loudly so the user knows custom voice cloning isn't happening, rather
        // than silently producing the default speaker.
        var usingReference = !string.IsNullOrEmpty(omniVoice.FilePath) && File.Exists(omniVoice.FilePath);
        if (usingReference)
        {
            var refTextPath = Path.ChangeExtension(omniVoice.FilePath, ".txt");
            if (!File.Exists(refTextPath))
            {
                throw new FileNotFoundException(
                    $"OmniVoice TTS voice cloning requires a transcript file at {refTextPath}. "
                    + "Re-import the voice to provide its transcript.",
                    refTextPath);
            }

            psi.ArgumentList.Add("--ref-wav");
            psi.ArgumentList.Add(omniVoice.FilePath);
            psi.ArgumentList.Add("--ref-text");
            psi.ArgumentList.Add(refTextPath);
        }

        // Voice-design keywords (--instruct) only shape the voice when there is no reference
        // audio - with --ref-wav omnivoice-tts takes the voice from the clone and ignores them.
        if (!usingReference)
        {
            var instruction = (Se.Settings.Video.TextToSpeech.OmniVoiceTtsCppInstruction ?? string.Empty).Trim();
            if (!string.IsNullOrEmpty(instruction))
            {

View on GitHub (pinned to 17a9f07487)

Solutions

  1. Re-import the voice via the Import Voice dialog and provide the transcript text — it writes the .txt sidecar automatically.
  2. Manually create the .txt file next to the voice WAV with the spoken transcription of the reference audio.
  3. If the transcript is unknown, use OmniVoiceCrispAsr.TryWriteRefTextSidecar(voiceWavPath, transcript) to write it after import.
  4. Alternatively, select the 'Default' voice (no FilePath) which does not require a transcript.

Example fix

// before — throws if the sidecar was deleted
var refTextPath = Path.ChangeExtension(omniVoice.FilePath, ".txt");
if (!File.Exists(refTextPath))
    throw new FileNotFoundException("...requires a transcript file at " + refTextPath);

// after — fall back to the default voice with a warning if transcript is missing
var refTextPath = Path.ChangeExtension(omniVoice.FilePath, ".txt");
if (usingReference && !File.Exists(refTextPath))
{
    Se.WriteToolsLog($"Transcript missing for {omniVoice.FilePath}; falling back to default voice.");
    usingReference = false;
}
Defensive patterns

Strategy: validation

Validate before calling

// Before synthesis, check for the transcript sidecar
if (!string.IsNullOrEmpty(omniVoice.FilePath) && File.Exists(omniVoice.FilePath))
{
    var refTextPath = Path.ChangeExtension(omniVoice.FilePath, ".txt");
    if (!File.Exists(refTextPath))
    {
        await PromptForTranscript(omniVoice.FilePath);
        return;
    }
}

Type guard

null

Try / catch

catch (FileNotFoundException ex) when (ex.Message.Contains("transcript file"))
{
    var path = ex.FileName; // the missing .txt path
    await PromptForTranscript(Path.ChangeExtension(path, ".wav"));
    // After providing transcript, retry
}

Prevention

When it happens

Trigger: usingReference is true (omniVoice.FilePath is non-empty and the WAV exists), but Path.ChangeExtension(omniVoice.FilePath, ".txt") does not exist. This happens when the transcript sidecar was manually deleted, or the voice was copied/imported without its transcript.

Common situations: The user deleted the .txt file next to a voice WAV to save space; the voice was imported via a path that didn't carry the transcript (ImportVoice with a null transcript and no source sidecar); a file-sync tool excluded .txt files but synced .wav files; the voice was seeded from another engine's folder but the sidecar filter rejected the text as an unusable attribution blurb.

Related errors


AI-assisted analysis of SubtitleEdit/subtitleedit@17a9f07487 (2026-08-13). Data as JSON: /api/errors/c4113de403fe4b13. Report an issue: GitHub.