Yeachan-Heo/oh-my-codex · error · Error
artifact_missing
artifact_missing
Error message
artifact_missing
What it means
Thrown as a plain Error with text "artifact_missing" when realpath() on the resolved artifact path fails with ENOENT — the file (or a directory component of the path) does not exist on disk. Callers are expected to match on the message text to detect missing artifacts.
Source
Thrown at src/mcp/hermes-bridge.ts:530
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;
}
throw new Error(`artifact path must be under ${SAFE_ARTIFACT_PREFIXES.join(", ")}`);
}
async function collectFiles(root: string, cwd: string, limit: number, out: Array<{ path: string; bytes: number }>): Promise<void> {
if (out.length >= limit || !existsSync(root)) return;
const cwdRealPath = await realpath(cwd);View on GitHub (pinned to 3ad79a8a6f)
Solutions
- Wait for or verify the step that produces the artifact completed before fetching
- Check existsSync/await stat on the file before calling the tool
- Catch the error and compare err.message === 'artifact_missing' to degrade gracefully
Example fix
// before
const res = await tool({ path: "artifacts/out.txt" });
// after
if (!existsSync(join(cwd, "artifacts/out.txt"))) throw new Error("artifact not produced yet");
const res = await tool({ path: "artifacts/out.txt" }); Defensive patterns
Strategy: type-guard
Validate before calling
import { existsSync } from 'node:fs';
if (!existsSync(path.join(cwd, relPath))) throw new Error('artifact not produced yet'); Type guard
async function artifactExists(cwd: string, rel: string): Promise<boolean> { try { await fs.stat(path.join(cwd, rel)); return true; } catch { return false; } } Try / catch
try { await readArtifact(cwd, p); } catch (e) { if ((e as Error).message === 'artifact_missing') return null; throw e; } Prevention
- Poll for file existence before fetching artifacts
- Treat artifact_missing as a retryable condition until a timeout
When it happens
Trigger: Requesting an artifact before the producing step has written it; typo in the filename; a symlink chain where an intermediate link is dangling (realpath fails with ENOENT).
Common situations: Race between agent finishing a build and the client fetching its output; artifacts cleaned up by a temp-dir sweeper; CI caching that excludes the artifacts directory.
Understand the failure class
Background: "File not found" and ENOENT errors: why libraries can't find a file that should exist — this error's family across 50 libraries.
Related errors
- artifact path must be relative
- artifact path must not traverse directories
- artifact path must be under ${SAFE_ARTIFACT_PREFIXES.join(",
- artifact resolved outside working directory
- autoresearch candidate artifact must be valid JSON
AI-assisted analysis of Yeachan-Heo/oh-my-codex@3ad79a8a6f (2026-08-27).
Data as JSON: /api/errors/99ccc97c1351f50a.
Report an issue: GitHub.