can1357/oh-my-pi · error
Invalid config file path: ${readPath}
Error message
Invalid config file path: ${readPath} What it means
#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.
Source
Thrown at packages/coding-agent/src/config/config-file.ts:258
stage: "createDefault",
});
}
#storeCache(result: LoadResult<T>): LoadResult<T> {
this.#cache = result;
return result;
}
#parseContent(content: string): LoadResult<T> {
try {
let parsed: unknown;
const readPath = this.#resolveReadPath();
if (readPath.endsWith(".json") || readPath.endsWith(".jsonc")) {
parsed = JSONC.parse(content);
} else if (readPath.endsWith(".yml") || readPath.endsWith(".yaml")) {
parsed = YAML.parse(content);
} else {
throw new Error(`Invalid config file path: ${readPath}`);
}
const checked = this.schema(parsed);
if (checked instanceof OmpErrors) {
const schemaErrors: ConfigSchemaError[] = checked.map(error => ({
instancePath: error.path.length === 0 ? "root" : error.path.join("."),
message: error.problem,
}));
const error = new ConfigError(this.id, schemaErrors);
logger.warn("Failed to parse config file", { path: this.path(), error });
return this.#storeCache({ error, status: "error" });
}
const value = checked as T;
try {
this.#auxValidate?.(value);
} catch (error) {
const wrapped =
error instanceof ConfigErrorView on GitHub (pinned to 9690622007)
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
Example fix
// before
new ConfigFile(id, schema, somePathWithUnknownExt).tryLoad();
// after
if (!/\.(ya?ml|jsonc?)$/.test(somePathWithUnknownExt)) throw new Error("Unsupported config extension");
new ConfigFile(id, schema, somePathWithUnknownExt).tryLoad(); Defensive patterns
Strategy: validation
Validate before calling
const readPath = configPath; // or the resolved fallback path
if (!/\.(ya?ml|jsonc?)$/.test(readPath)) {
throw new Error(`Refusing to load config with unsupported extension: ${readPath}`);
} Type guard
const hasSupportedConfigExt = (p: string): boolean => /\.(ya?ml|jsonc?)$/.test(p);
Try / catch
try {
result = configFile.tryLoad();
} catch (err) {
if (err instanceof Error && err.message.startsWith("Invalid config file path")) {
logger.warn("Unparseable config path; using defaults", { err });
result = { status: "error", value: null, error: err };
} else throw err;
} Prevention
- 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
When it happens
Trigger: 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.
Common situations: Rare in practice; seen with subclasses/mocks of ConfigFile, programmatic construction with dynamic paths, or external tooling (dotfile managers) renaming config files.
Related errors
- Unsupported keybindings config extension: ${filePath}
- Failed to parse config overlay ${filePath}: ${String(error)}
- Replacement text is not valid UTF-8: {err}
- Failed to load tree-sitter language: {err}
- V2 compaction stream parse failed: ${err instanceof Error ?
AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31).
Data as JSON: /api/errors/b3e911cf5da1c7a3.
Report an issue: GitHub.