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 workingView on GitHub (pinned to 9690622007)
Solutions
- Use the 'Available: ...' ids listed in the message and retry with one of those ids
- List all artifacts first (iterate the artifacts directory or the artifact:// listing) to confirm valid ids
- 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
- Capture artifact ids from the tool result that created them instead of guessing
- List available artifact ids before resolving
- Keep artifact references together with the session that produced them
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
- Plugin ${name} not found in runtime config
- Marketplace "${name}" not found
- Marketplace "${marketplace}" not found
- Plugin "${name}" not found in marketplace "${marketplace}"
- Plugin "${pluginId}" is not installed
AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31).
Data as JSON: /api/errors/8a5a6ebcf4fc3104.
Report an issue: GitHub.