Yeachan-Heo/oh-my-codex · error · Error
artifact resolved outside working directory
Error message
artifact resolved outside working directory
What it means
Thrown by resolveSafeArtifactPath when resolving the artifact path against the working directory produces a path that escapes it (relativeToCwd starts with ".." or is absolute). This is a defense-in-depth check after lexical normalization, before any filesystem access.
Source
Thrown at src/mcp/hermes-bridge.ts:523
throw new Error("artifact path must not traverse directories");
}
if (!SAFE_ARTIFACT_PREFIXES.some((prefix) => normalized.startsWith(prefix))) {
throw new Error(`artifact path must be under ${SAFE_ARTIFACT_PREFIXES.join(", ")}`);
}
return normalized;
}
function isInsideDirectory(parent: string, candidate: string): boolean {
const rel = relative(parent, candidate);
return rel === "" || (!rel.startsWith("..") && !isAbsolute(rel));
}
async function resolveSafeArtifactPath(cwd: string, rel: string): Promise<string> {
const cwdRealPath = await realpath(cwd);
const full = resolve(cwd, rel);
const relativeToCwd = relative(resolve(cwd), full);
if (relativeToCwd.startsWith("..") || isAbsolute(relativeToCwd)) {
throw new Error("artifact resolved outside working directory");
}
let artifactRealPath: string;
try {
artifactRealPath = await realpath(full);
} catch (error) {
if ((error as NodeJS.ErrnoException).code === "ENOENT") throw new Error("artifact_missing");
throw error;
}
if (!isInsideDirectory(cwdRealPath, artifactRealPath)) {
throw new Error("artifact resolved outside working directory");
}
for (const prefix of SAFE_ARTIFACT_PREFIXES) {
const rootRealPath = await realpath(resolve(cwd, prefix)).catch(() => null);
if (rootRealPath && isInsideDirectory(rootRealPath, artifactRealPath)) return artifactRealPath;
}View on GitHub (pinned to 3ad79a8a6f)
Solutions
- Simplify the path before sending (path.normalize) and drop any leading ../
- Ensure the artifact genuinely lives inside the working directory
- Verify the workingDirectory argument itself is the project root you expect
Defensive patterns
Strategy: try-catch
Validate before calling
const n = path.normalize(p); if (n.startsWith('..') || path.isAbsolute(n)) throw new Error('escapes cwd'); Try / catch
try { await resolveArtifact(cwd, p); } catch (e) { if ((e as Error).message === 'artifact resolved outside working directory') {/* re-request a safe path */} throw e; } Prevention
- path.normalize inputs before sending
- Confirm workingDirectory points at the real project root
When it happens
Trigger: A relative path that survives the earlier checks but still resolves outside cwd, e.g. via symlink-like segments or a race where cwd changed; effectively any path whose resolve(cwd, rel) sits outside cwd.
Common situations: Unusual path inputs like "a/../../.."; working directories that are themselves symlinks causing resolve/relative mismatches; crafted inputs in security testing.
Understand the failure class
Background: Path traversal blocked: "path escapes the workspace" and "outside site root" errors when a path will not stay inside its allowed directory — this error's family across 26 libraries.
Related errors
- artifact path must not traverse directories
- artifact path must be relative
- mode must not contain ".."
- fileName must not contain ".."
- run directory escapes the authorized runs root
AI-assisted analysis of Yeachan-Heo/oh-my-codex@3ad79a8a6f (2026-08-27).
Data as JSON: /api/errors/91b6053798866304.
Report an issue: GitHub.