{"record":{"id":"b4bf3547b063017c","repo":"babalae/better-genshin-impact","slug":"paddleocr-config-file-modelconfigfilename-not-fo","errorCode":null,"errorMessage":"PaddleOCR config file {modelConfigFileName} not found: {configFilePath}","messagePattern":"PaddleOCR config file (.+?) not found: (.+?)","errorType":"exception","errorClass":"FileNotFoundException","httpStatus":null,"severity":"critical","filePath":"BetterGenshinImpact/Core/Recognition/OCR/Paddle/PaddleOcrService.cs","lineNumber":55,"sourceCode":"        String PreHeatImagePath\n    )\n    {\n        public static string TestImagePath = Global.Absolute(@\"Assets\\Model\\PaddleOCR\\test_pp_ocr.png\");\n\n        public static string TestNumberImagePath =\n            Global.Absolute(@\"Assets\\Model\\PaddleOCR\\test_pp_ocr_number.png\");\n\n        private static readonly Func<BgiOnnxModel, IReadOnlyList<string>> DefaultRecLabelFunc =\n            recModel =>\n            {\n                const string modelConfigFileName = \"inference.yml\";\n                var configFilePath = Path.Combine(\n                    Path.GetDirectoryName(recModel.ModalPath) ??\n                    throw new InvalidOperationException(\"Cannot get model directory\"),\n                    modelConfigFileName);\n\n                if (!File.Exists(configFilePath))\n                    throw new FileNotFoundException(\n                        $\"PaddleOCR config file {modelConfigFileName} not found: {configFilePath}\");\n\n                using var reader = new StreamReader(configFilePath);\n                var parser = new Parser(reader);\n\n                // Traverse YAML to find PostProcess:character_dict\n                while (parser.MoveNext())\n                {\n                    if (parser.Current is not YamlDotNet.Core.Events.Scalar { Value: \"PostProcess\" }) continue;\n                    parser.MoveNext(); // Should be MappingStart\n                    while (parser.MoveNext())\n                    {\n                        if (parser.Current is not YamlDotNet.Core.Events.Scalar { Value: \"character_dict\" }) continue;\n                        parser.MoveNext(); // Should be SequenceStart\n                        var result = new List<string>();\n                        while (parser.MoveNext())\n                        {\n                            switch (parser.Current)","sourceCodeStart":37,"sourceCodeEnd":73,"githubUrl":"https://github.com/babalae/better-genshin-impact/blob/a7cb36712dcb409be610257d877fcea3597e9d6b/BetterGenshinImpact/Core/Recognition/OCR/Paddle/PaddleOcrService.cs#L37-L73","documentation":"PaddleOcrService loads the OCR character alphabet from inference.yml, which must sit next to the recognition ONNX model (path derived from BgiOnnxModel.ModalPath). This FileNotFoundException fires when that YAML config is missing, so the recognizer has no character dictionary and the service cannot initialize. It is raised inside DefaultRecLabelFunc, which every built-in model type (V4/V5/V6/latin/eslav/korean) uses unless a custom recLabel is supplied.","triggerScenarios":"Constructing PaddleOcrService with a PaddleOcrModelType whose recognition model directory contains slim.onnx but not inference.yml. Happens during the Build() call that wires up the Rec model, i.e. inside the PaddleOcrService constructor.","commonSituations":"Model package repackaged without the .yml; .gitignore or packaging step filtering out .yml files; partial Git checkout; model folder moved so ModalPath no longer points at the real directory; case-sensitivity mismatch on the file name.","solutions":["Restore inference.yml into the recognition model folder, e.g. Assets\\Model\\PaddleOCR\\Rec\\V5\\PP-OCRv5_mobile_rec_infer\\inference.yml.","Confirm the ModelRelativePath registered in BgiOnnxModel (e.g. PaddleOcrRecV5) matches the on-disk folder where slim.onnx and inference.yml actually live.","Check .gitignore / packaging rules are not excluding *.yml next to *.onnx.","If the YAML is unavailable, build the model type via Create(...) with an explicit recLabel that returns the character list from another source."],"exampleFix":"// before: model folder ships slim.onnx only -> DefaultRecLabelFunc throws\nnew PaddleOcrService(factory, PaddleOcrModelType.V5);\n\n// after: validate the sidecar config before constructing\nvar rec = PaddleOcrModelType.V5.RecognitionModel;\nvar yml = Path.Combine(Path.GetDirectoryName(rec.ModalPath)!, \"inference.yml\");\nif (!File.Exists(yml)) throw new InvalidOperationException($\"Missing OCR config {yml}; restore the model package.\");\nnew PaddleOcrService(factory, PaddleOcrModelType.V5);","handlingStrategy":"validation","validationCode":"bool IsPaddleConfigReady(BgiOnnxModel recModel)\n{\n    var dir = Path.GetDirectoryName(recModel.ModalPath);\n    if (dir is null) return false;\n    return File.Exists(Path.Combine(dir, \"inference.yml\"));\n}\n\n// before constructing the service:\nif (!IsPaddleConfigReady(PaddleOcrModelType.V5.RecognitionModel))\n    throw new InvalidOperationException(\"PaddleOCR inference.yml missing next to the recognition model.\");","typeGuard":null,"tryCatchPattern":"try\n{\n    _ocr = new PaddleOcrService(factory, PaddleOcrModelType.V5);\n}\ncatch (FileNotFoundException ex) when (ex.FileName?.EndsWith(\"inference.yml\", StringComparison.OrdinalIgnoreCase) == true\n    || ex.Message.Contains(\"inference.yml\", StringComparison.OrdinalIgnoreCase))\n{\n    Logger.LogError(ex, \"PaddleOCR config missing; OCR disabled.\");\n    _ocr = null; // or fall back to an alternate IOcrService\n}","preventionTips":["Treat inference.yml as a required sibling of each PaddleOCR recognition .onnx; ship them together.","Add a startup asset check that walks BgiOnnxModel.GetAll() and verifies expected sidecar configs.","Do not .gitignore *.yml under Assets\\Model\\PaddleOCR.","Pin model package versions so partial updates cannot drop the config."],"tags":["paddle-ocr","onnx","asset-missing","config","yaml"],"backgroundTag":null,"analyzedSha":"a7cb36712dcb409be610257d877fcea3597e9d6b","analyzedAt":"2026-08-13T16:44:57.548Z","schemaVersion":2},"datasetVersion":"2026-08-13T19:17:28.613Z"}