can1357/oh-my-pi · error · ConfigError
Validate(${name})
Error message
Validate(${name}) What it means
withValidation lets you attach a named auxiliary validator on top of the schema. If your validate callback throws, the library wraps the throw into a ConfigError whose stage is `Validate(name)`, attributing the failure to your named check rather than schema parsing. The original error is preserved as the cause (err).
Source
Thrown at packages/coding-agent/src/config/config-file.ts:227
throw err;
}
}
async getMtimeMsAsync(): Promise<number | null> {
const file = Bun.file(this.path());
if (!(await file.exists())) return null;
const lm = file.lastModified;
return typeof lm === "number" && Number.isFinite(lm) ? lm : null;
}
withValidation(name: string, validate: (value: T) => void): this {
const prev = this.#auxValidate;
this.#auxValidate = (value: T) => {
prev?.(value);
try {
validate(value);
} catch (error) {
throw new ConfigError(this.id, undefined, { err: error, stage: `Validate(${name})` });
}
};
return this;
}
createDefault(): T {
const parsed = this.schema({});
if (!(parsed instanceof Error)) return parsed as T;
const fallback = this.schema(undefined);
if (!(fallback instanceof Error)) return fallback as T;
throw new ConfigError(this.id, undefined, {
err: new Error("Schema produced no default value"),
stage: "createDefault",
});
}
#storeCache(result: LoadResult<T>): LoadResult<T> {
this.#cache = result;View on GitHub (pinned to 9690622007)
Solutions
- Read the wrapped `err` in the ConfigError to see the actual validation failure and fix the config value it names
- Fix the config so the named cross-field/semantic constraint passes (e.g. remove the conflicting key)
- If the validator itself is buggy, fix or loosen the callback passed to withValidation
Example fix
// before (config) autosave: true autosaveInterval: 0 // after (config) autosave: true autosaveInterval: 30
Defensive patterns
Strategy: try-catch
Validate before calling
// run the same semantic checks before load
function preValidate(value: MyConfig): string | null {
if (value.autosave && value.autosaveInterval <= 0) return "autosaveInterval must be > 0 when autosave is on";
return null;
} Type guard
function isConfigError(e: unknown): e is ConfigError {
return e instanceof ConfigError;
} Try / catch
try {
result = configFile.tryLoad();
} catch (err) {
if (err instanceof ConfigError && err.stage === "Validate(crossField)") {
logger.warn("Config failed semantic validation", { cause: err.err });
value = configFile.createDefault();
} else throw err;
} Prevention
- Keep withValidation callbacks deterministic and throwing clear, actionable messages
- Prefer schema-level defaults/constraints over auxiliary validators when possible
- Dry-run the semantic check on sample values during tests
- Inspect err.err (the wrapped cause) to fix the exact offending config field
When it happens
Trigger: Registering `.withValidation("crossField", fn)` and then loading a config whose parsed value makes `fn(value)` throw — e.g. mutually exclusive keys both set, an invalid port range, a reference to a nonexistent file. Applied by tryLoad/tryLoadAsync after schema validation.
Common situations: Semantic checks a schema cannot express: conflicting settings, deprecated option combinations, cross-field range violations, or a validator that throws unexpectedly on valid-but-unusual data.
Understand the failure class
Background: Config validation failed: what "invalid value for {key}" and settings-rejection errors mean across 19 open-source libraries — this error's family across 19 libraries.
Related errors
- Failed to load tree-sitter language: {err}
- Destination option ${key} must be a string
- Destination option ${key} must be a finite number
- Destination option ${key} must be a boolean
- ${destination} returned an invalid upload URL
AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31).
Data as JSON: /api/errors/ead786d7ee6b90fa.
Report an issue: GitHub.