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 ConfigError

View on GitHub (pinned to 9690622007)

Solutions

  1. Ensure the config file uses .yml, .yaml, .json, or .jsonc and construct a fresh ConfigFile with it
  2. Inspect #resolveReadPath outcomes: remove or rename any stray file at the base/fallback path with an unsupported extension
  3. 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

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


AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31). Data as JSON: /api/errors/b3e911cf5da1c7a3. Report an issue: GitHub.