can1357/oh-my-pi · error

Artifact ${id} not found. Available: ${availableStr}

Error message

Artifact ${id} not found. Available: ${availableStr}

What it means

resolveArtifactFile scans the session artifacts directory for a file whose id matches the requested artifact id. If no file matches after normalizing ids, it throws with the sorted list of ids that DO exist (or 'none') so the caller can pick a valid one. It exists to turn a bad artifact:// URL into an actionable message instead of a silent null.

Source

Thrown at packages/coding-agent/src/internal-urls/artifact-protocol.ts:88

		const match = files.find(f => f.startsWith(`${id}.`));
		if (match) {
			foundPath = path.join(dir, match);
			break;
		}
		for (const f of files) {
			const m = f.match(/^(\d+)\./);
			if (m) availableIds.add(m[1]);
		}
	}

	if (!anyDirExists) {
		throw new Error("No artifacts directory found");
	}

	if (!foundPath) {
		const sorted = [...availableIds].sort((a, b) => Number(a) - Number(b));
		const availableStr = sorted.length > 0 ? sorted.join(", ") : "none";
		throw new Error(`Artifact ${id} not found. Available: ${availableStr}`);
	}

	const stat = await Bun.file(foundPath).stat();
	if (stat.isDirectory()) {
		throw new Error(`Artifact ${id} resolved to a directory, not a file`);
	}
	return { id, path: foundPath, size: stat.size };
}

export class ArtifactProtocolHandler implements ProtocolHandler {
	readonly scheme = "artifact";
	readonly immutable = true;

	async resolve(url: InternalUrl, context?: ResolveContext): Promise<InternalResource> {
		const artifact = await resolveArtifactFile(url, context);

		// Path-only callers (search/grep, bash URL expansion) never touch the
		// artifact bytes. Return the resource shape so those flows keep working

View on GitHub (pinned to 9690622007)

Solutions

  1. Use the 'Available: ...' ids listed in the message and retry with one of those ids
  2. List all artifacts first (iterate the artifacts directory or the artifact:// listing) to confirm valid ids
  3. If the artifact should exist, check you are pointing at the correct session's artifacts directory

Example fix

// before
const doc = await resolve(`artifact://1042:1-200`)
// after
const doc = await resolve(`artifact://1041:1-200`) // 1041 listed as available
Defensive patterns

Strategy: fallback

Validate before calling

import { readdir } from 'node:fs/promises'
const ids = (await readdir(artifactsDir)).map(f => f.replace(/\.\w+$/, ''))
if (!ids.includes(wantedId)) throw new Error(`artifact id ${wantedId} not in [${ids.join(', ')}]`)

Try / catch

try {
  const art = await resolve(`artifact://${id}`)
} catch (err) {
  const avail = /Available: (.+)$/.exec(String(err))?.[1]
  if (avail && avail !== 'none') return resolve(`artifact://${avail.split(',')[0].trim()}`)
  throw err
}

Prevention

When it happens

Trigger: Calling artifact://<id> (via resolveArtifactFile/artifact) with an id that is not in the artifacts directory — e.g. a stale id from a previous session, a typo, or referencing artifacts of a different session whose directory is being scanned.

Common situations: Agent referenced an artifact id from an old transcript after artifacts were cleaned up; user hand-wrote an artifact:// URL guessing the id; artifacts directory was pruned between tool call and read.

Related errors


AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31). Data as JSON: /api/errors/8a5a6ebcf4fc3104. Report an issue: GitHub.