{"record":{"id":"b3e911cf5da1c7a3","repo":"can1357/oh-my-pi","slug":"invalid-config-file-path-readpath","errorCode":null,"errorMessage":"Invalid config file path: ${readPath}","messagePattern":"Invalid config file path: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/coding-agent/src/config/config-file.ts","lineNumber":258,"sourceCode":"\t\t\tstage: \"createDefault\",\n\t\t});\n\t}\n\n\t#storeCache(result: LoadResult<T>): LoadResult<T> {\n\t\tthis.#cache = result;\n\t\treturn result;\n\t}\n\n\t#parseContent(content: string): LoadResult<T> {\n\t\ttry {\n\t\t\tlet parsed: unknown;\n\t\t\tconst readPath = this.#resolveReadPath();\n\t\t\tif (readPath.endsWith(\".json\") || readPath.endsWith(\".jsonc\")) {\n\t\t\t\tparsed = JSONC.parse(content);\n\t\t\t} else if (readPath.endsWith(\".yml\") || readPath.endsWith(\".yaml\")) {\n\t\t\t\tparsed = YAML.parse(content);\n\t\t\t} else {\n\t\t\t\tthrow new Error(`Invalid config file path: ${readPath}`);\n\t\t\t}\n\n\t\t\tconst checked = this.schema(parsed);\n\t\t\tif (checked instanceof OmpErrors) {\n\t\t\t\tconst schemaErrors: ConfigSchemaError[] = checked.map(error => ({\n\t\t\t\t\tinstancePath: error.path.length === 0 ? \"root\" : error.path.join(\".\"),\n\t\t\t\t\tmessage: error.problem,\n\t\t\t\t}));\n\t\t\t\tconst error = new ConfigError(this.id, schemaErrors);\n\t\t\t\tlogger.warn(\"Failed to parse config file\", { path: this.path(), error });\n\t\t\t\treturn this.#storeCache({ error, status: \"error\" });\n\t\t\t}\n\t\t\tconst value = checked as T;\n\t\t\ttry {\n\t\t\t\tthis.#auxValidate?.(value);\n\t\t\t} catch (error) {\n\t\t\t\tconst wrapped =\n\t\t\t\t\terror instanceof ConfigError","sourceCodeStart":240,"sourceCodeEnd":276,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/coding-agent/src/config/config-file.ts#L240-L276","documentation":"#parseContent chooses a parser (JSONC for .json/.jsonc, YAML for .yml/.yaml) based on the resolved read path's extension. If the existing file (base path or .yaml fallback) has a different extension, no parser can be selected and it throws. This is defensive: the constructor normally rejects such paths, so hitting this means the read path resolved to an unexpected file.","triggerScenarios":"tryLoad/tryLoadAsync reading a config whose #resolveReadPath() result ends in an unsupported extension — e.g. a stray file was renamed to an unknown extension at the base path, or an instance was created through a path bypassing the constructor check.","commonSituations":"Rare in practice; seen with subclasses/mocks of ConfigFile, programmatic construction with dynamic paths, or external tooling (dotfile managers) renaming config files.","solutions":["Ensure the config file uses .yml, .yaml, .json, or .jsonc and construct a fresh ConfigFile with it","Inspect #resolveReadPath outcomes: remove or rename any stray file at the base/fallback path with an unsupported extension","Validate the extension programmatically before constructing/loading rather than after"],"exampleFix":"// before\nnew ConfigFile(id, schema, somePathWithUnknownExt).tryLoad();\n// after\nif (!/\\.(ya?ml|jsonc?)$/.test(somePathWithUnknownExt)) throw new Error(\"Unsupported config extension\");\nnew ConfigFile(id, schema, somePathWithUnknownExt).tryLoad();","handlingStrategy":"validation","validationCode":"const readPath = configPath; // or the resolved fallback path\nif (!/\\.(ya?ml|jsonc?)$/.test(readPath)) {\n\tthrow new Error(`Refusing to load config with unsupported extension: ${readPath}`);\n}","typeGuard":"const hasSupportedConfigExt = (p: string): boolean => /\\.(ya?ml|jsonc?)$/.test(p);","tryCatchPattern":"try {\n\tresult = configFile.tryLoad();\n} catch (err) {\n\tif (err instanceof Error && err.message.startsWith(\"Invalid config file path\")) {\n\t\tlogger.warn(\"Unparseable config path; using defaults\", { err });\n\t\tresult = { status: \"error\", value: null, error: err };\n\t} else throw err;\n}","preventionTips":["Never mutate the base path of an existing ConfigFile; construct a new instance with a validated path","Keep only .yml/.yaml/.json/.jsonc files in the config directory","Verify file extensions after external tooling (sync scripts, dotfile managers) touches the config directory"],"tags":["config","parsing","unsupported-extension"],"backgroundTag":"unsupported-config-file-extension","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}