SubtitleEdit/subtitleedit · error · ForcedAlignerException

The forced aligner failed (exit code {process.ExitCode}).

Error message

The forced aligner failed (exit code {process.ExitCode}).

What it means

The generic catch-all from CrispAsrAlignOnlyRunner.AlignAsync: the aligner exited non-zero (or wrote no .aligned.srt) and the stderr did not match the out-of-memory signature, so the runner wraps the exit code plus captured stderr into a ForcedAlignerException.

Source

Thrown at src/ui/Features/Files/ImportPlainText/CrispAsrAlignOnlyRunner.cs:99

        {
            TryKill(process);
            throw;
        }

        if (process.ExitCode != 0 || !File.Exists(outputFileName))
        {
            var detail = stdErr.ToString().Trim();
            _log?.Invoke(detail);

            // The aligner allocates its encoder activations up front, so a window that is
            // too long for the available memory fails here rather than degrading.
            if (detail.Contains("failed to allocate", StringComparison.OrdinalIgnoreCase))
            {
                throw new ForcedAlignerException(
                    "The forced aligner ran out of memory on this window. Try a shorter window length.", detail);
            }

            throw new ForcedAlignerException($"The forced aligner failed (exit code {process.ExitCode}).", detail);
        }

        return await File.ReadAllTextAsync(outputFileName, cancellationToken).ConfigureAwait(false);
    }

    private static void TryKill(Process process)
    {
        try
        {
            if (!process.HasExited)
            {
                process.Kill(true);
            }
        }
        catch
        {
            // Killing a process that just exited on its own is not an error.
        }

View on GitHub (pinned to 17a9f07487)

Solutions

  1. Inspect the Detail property of the ForcedAlignerException — it carries the aligner's stderr verbatim, which states the real cause.
  2. Confirm the -am model path exists and matches the aligner version.
  3. Confirm the input WAV is 16 kHz mono PCM as the aligner expects.
  4. Confirm the text file is non-empty and UTF-8.
  5. Re-run with the aligner directly to reproduce, then fix the reported error.

Example fix

// surface the detail
catch (ForcedAlignerException ex) {
    logger.Error($"aligner exit {ex.Detail}");
}
Defensive patterns

Strategy: try-catch

Validate before calling

// Validate model + audio before invoking
if (!File.Exists(_alignerModel)) throw new ArgException($"Aligner model not found: {_alignerModel}");
if (!File.Exists(audioFileName)) throw new ArgException($"Audio not found: {audioFileName}");
if (new FileInfo(audioFileName).Length == 0) throw new ArgException("Audio file is empty.");

Type guard

null

Try / catch

try { return await runner.AlignAsync(a, t, ct); }
catch (ForcedAlignerException ex) { logger.Error($"aligner failed: {ex.Message}\ndetail: {ex.Detail}"); throw; }

Prevention

When it happens

Trigger: process.ExitCode != 0 || !File.Exists(outputFileName), and the stderr detail does not contain 'failed to allocate'.

Common situations: Missing or wrong -am model path; corrupt/unreadable audio; text file length mismatch; unsupported sample rate; aligner binary crash; model/text encoding mismatch; cancelled mid-run leaving no output.

Related errors


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