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

  1. Wait for or verify the step that produces the artifact completed before fetching
  2. Check existsSync/await stat on the file before calling the tool
  3. 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

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


AI-assisted analysis of Yeachan-Heo/oh-my-codex@3ad79a8a6f (2026-08-27). Data as JSON: /api/errors/99ccc97c1351f50a. Report an issue: GitHub.