{"record":{"id":"84a06e379f79c45c","repo":"SubtitleEdit/subtitleedit","slug":"paddleocr-exited-with-code-proc-exitcode-err","errorCode":null,"errorMessage":"paddleocr exited with code {proc.ExitCode}: {err}","messagePattern":"paddleocr exited with code (.+?): (.+?)","errorType":"exception","errorClass":"InvalidOperationException","httpStatus":null,"severity":"error","filePath":"src/seconv/Core/PaddleOcrEngine.cs","lineNumber":102,"sourceCode":"            // StandardOutputEncoding only fixes the decoding side. paddleocr is a Python CLI, and\n            // on Windows Python encodes a *redirected* stdout with the ANSI codepage (until UTF-8\n            // becomes the default in Python 3.15, PEP 686) - so the producer side must be forced\n            // to UTF-8 too, or non-ASCII text still arrives as mojibake. Same env vars the UI's\n            // Paddle engine sets.\n            psi.EnvironmentVariables[\"PYTHONIOENCODING\"] = \"utf-8\";\n            psi.EnvironmentVariables[\"PYTHONUTF8\"] = \"1\";\n\n            using var proc = Process.Start(psi)\n                ?? throw new InvalidOperationException(\"Failed to start paddleocr process.\");\n            // Drain stderr concurrently — paddleocr is chatty on stderr, and reading stdout\n            // to completion while stderr fills the pipe buffer would deadlock.\n            var stderrTask = proc.StandardError.ReadToEndAsync();\n            var stdout = proc.StandardOutput.ReadToEnd();\n            proc.WaitForExit();\n            if (proc.ExitCode != 0)\n            {\n                var err = stderrTask.GetAwaiter().GetResult();\n                throw new InvalidOperationException($\"paddleocr exited with code {proc.ExitCode}: {err}\");\n            }\n            return ParseStdout(stdout);\n        }\n        finally\n        {\n            try { File.Delete(pngPath); } catch { /* best-effort */ }\n        }\n    }\n\n    /// <summary>\n    /// Parses paddleocr's stdout. The CLI prints one or more <c>[bbox], (text, conf)</c>\n    /// records; we extract just the recognised text from each, joining with newlines in\n    /// vertical order.\n    /// </summary>\n    internal static string ParseStdout(string stdout)\n    {\n        // Match: ('text', 0.95)  -- the recognised text is before the comma in single quotes.\n        var sb = new StringBuilder();","sourceCodeStart":84,"sourceCodeEnd":120,"githubUrl":"https://github.com/SubtitleEdit/subtitleedit/blob/17a9f0748781032255db3526b7215d2fb891e3af/src/seconv/Core/PaddleOcrEngine.cs#L84-L120","documentation":"Thrown by PaddleOcrEngine.Recognize when the paddleocr subprocess exits with a non-zero code. The message embeds the exit code and the full stderr captured concurrently, so the upstream Python traceback is visible. This is a paddleocr runtime failure, not a seconv failure.","triggerScenarios":"Calling PaddleOcrEngine.Recognize (via `--ocr-engine paddle`) where paddleocr starts but errors: missing model download, CUDA/CPU mismatch, incompatible PaddlePaddle version, corrupt input image, language model not downloaded.","commonSituations":"First run where PaddleOCR needs to download a language model and the network/proxy blocks it; PaddlePaddle GPU build on a CPU-only machine; image passed as an unsupported format; paddleocr and paddlepaddle version skew.","solutions":["Read the stderr in the error message — it pinpoints the Python-level cause.","Pre-download the language model by running `paddleocr ocr -l <lang>` once interactively.","Align paddleocr and paddlepaddle versions (`pip install -U paddleocr paddlepaddle`).","For GPU/CPU issues, install `paddlepaddle` (CPU) instead of `paddlepaddle-gpu`.","Confirm the input subtitle actually produced a non-empty bitmap (the engine skips null bitmaps earlier)."],"exampleFix":"# before: paddleocr crashes with model download error\nseconv in.sup out.srt --ocr-engine paddle\n# after: pre-warm the model\npaddleocr ocr -l en -i dummy.png\nseconv in.sup out.srt --ocr-engine paddle","handlingStrategy":"try-catch","validationCode":"// No purely-local precheck; pre-warm the model instead:\n// var psi = new ProcessStartInfo(\"paddleocr\"){ ArgumentList={\"ocr\",\"-l\",lang,\"-i\",warmupPng}};\n// run it once and ensure ExitCode==0 before the batch.","typeGuard":null,"tryCatchPattern":"try { text = engine.Recognize(bitmap); }\ncatch (InvalidOperationException ex) when (ex.Message.Contains(\"paddleocr exited with code\"))\n{\n    // log ex (contains stderr), skip this subtitle, continue batch\n}","preventionTips":["Pre-warm paddleocr with one dummy image so models download before the batch.","Pin compatible paddleocr + paddlepaddle versions in requirements.","Log the embedded stderr verbatim for post-mortem."],"tags":["ocr","paddleocr","process","external-service","python","runtime-error"],"backgroundTag":null,"analyzedSha":"17a9f0748781032255db3526b7215d2fb891e3af","analyzedAt":"2026-08-13T18:11:43.374Z","schemaVersion":2},"datasetVersion":"2026-08-13T19:17:28.613Z"}