can1357/oh-my-pi · error · ValidationError
${name} path does not exist: ${trimmed}
Error message
${name} path does not exist: ${trimmed} What it means
resolvePemValue accepts a TLS/SSH key material either inline (a PEM string containing '-----BEGIN') or as a file path. When the value looks like a path (contains / or \ or ends in .pem/.crt/.cer/.key) but reading it fails with ENOENT, the library throws a ValidationError naming the parameter and the path. This is a configuration error: the caller pointed a cert/key option at a file that does not exist.
Source
Thrown at packages/ai/src/providers/anthropic.ts:1300
function looksLikeFilePath(value: string): boolean {
return value.includes("/") || value.includes("\\") || /\.(pem|crt|cer|key)$/i.test(value);
}
function resolvePemValue(value: string | undefined, name: string): string | undefined {
const trimmed = value?.trim();
if (!trimmed) return undefined;
const inline = trimmed.replace(/\\n/g, "\n");
if (inline.includes("-----BEGIN")) {
return inline;
}
if (looksLikeFilePath(trimmed)) {
try {
return fs.readFileSync(trimmed, "utf8");
} catch (error) {
if (isEnoent(error)) {
throw new AIError.ValidationError(`${name} path does not exist: ${trimmed}`);
}
throw error;
}
}
return inline;
}
function resolveFoundryTlsOptions(model: Model<"anthropic-messages">): FoundryTlsOptions | undefined {
if (model.provider !== "anthropic") return undefined;
if (!isFoundryEnabled()) return undefined;
const cacheKey = foundryTlsOptionsCacheKey();
if (foundryTlsOptionsCache.has(cacheKey)) return foundryTlsOptionsCache.get(cacheKey);
const ca = resolvePemValue($env.NODE_EXTRA_CA_CERTS, "NODE_EXTRA_CA_CERTS");
const cert = resolvePemValue($env.CLAUDE_CODE_CLIENT_CERT, "CLAUDE_CODE_CLIENT_CERT");
const key = resolvePemValue($env.CLAUDE_CODE_CLIENT_KEY, "CLAUDE_CODE_CLIENT_KEY");View on GitHub (pinned to 9690622007)
Solutions
- Verify the file exists at the exact path given (ls the path with the same user/working directory the app runs as)
- Use absolute paths in configuration so the app's CWD cannot change resolution
- If embedding the key inline, ensure it contains the full '-----BEGIN ... PRIVATE KEY-----' header and real newlines (or literal \n escapes)
- In containers, confirm the secret mount and volume mountPath match the configured path
- Catch this ValidationError at startup and fail fast with a clear config-error message instead of at first request
Example fix
// before: relative path, breaks when CWD changes
tls: { keyFile: "certs/client-key.pem" }
// after: absolute path + existence check at boot
const keyPath = path.resolve(process.env.KEY_DIR ?? "/etc/app/certs", "client-key.pem");
if (!fs.existsSync(keyPath)) throw new Error(`Missing TLS key: ${keyPath}`);
tls: { keyFile: keyPath } Defensive patterns
Strategy: validation
Validate before calling
import * as fs from "node:fs";
function assertPemReadable(name: string, value: string | undefined): void {
const trimmed = value?.trim();
if (!trimmed) return;
if (trimmed.includes("-----BEGIN")) return;
if (trimmed.includes("/") || trimmed.includes("\\") || /\.(pem|crt|cer|key)$/i.test(trimmed)) {
if (!fs.existsSync(trimmed)) throw new Error(`${name} path does not exist: ${trimmed}`);
}
} Type guard
function isPemMaterial(value: string): boolean {
return value.includes("-----BEGIN");
} Try / catch
try {
await createProvider(opts);
} catch (err) {
if (err instanceof AIError.ValidationError && err.message.includes("path does not exist")) {
// config error: cert/key file missing; fail fast with actionable guidance
throw new Error(`TLS config error — ${err.message}. Check secret mounts and use absolute paths.`);
}
throw err;
} Prevention
- Use absolute paths for cert/key files and verify them at startup, not first request
- Confirm container secret mounts match configured paths in staging before prod
- When inlining PEM, keep the BEGIN header intact so it isn't misparsed as a path
- Beware escaped-\n env vars: unescape them consistently before passing key material
When it happens
Trigger: Passing a path to a PEM option (client certificate/key, CA bundle for Foundry TLS, mTLS settings) where the file is missing — wrong relative path, file deleted, secret not mounted in the container, or an inline key whose newlines were mangled so it no longer contains '-----BEGIN' and is therefore treated as a path.
Common situations: Kubernetes/containers where the secret volume wasn't mounted at the expected path; env vars set with escaped \n that collapsed incorrectly; running from a different working directory so relative paths break; copying config between machines where cert files live elsewhere.
Related errors
- OMP_AUTH_BROKER_ACCOUNT_POOL_FILE contains an empty provider
- OMP_AUTH_BROKER_ACCOUNT_POOL_FILE contains a provider id wit
- Anthropic thinking budget requires max_tokens greater than $
- cachedContent must not be blank
- imageUrls exposure "bore" requires options.server
AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31).
Data as JSON: /api/errors/a5730d5142eaba94.
Report an issue: GitHub.