Yeachan-Heo/oh-my-codex · critical · Error
native_agent_canonical_invalid
native_agent_canonical_invalid
Error message
native_agent_canonical_invalid
agent=${agent.name}
message=alias/merged native agents must declare a canonical target What it means
Thrown when the member entry to write exists but reports size zero, so the library refuses to hydrate it. An empty binary written to disk would create a broken executable, so validation happens before any file I/O.
Source
Thrown at src/agents/policy.ts:69
promptName: string,
manifest: Pick<CatalogManifest, "agents">,
): boolean {
return (
manifest.agents.some((agent) => agent.name === promptName) ||
NON_NATIVE_AGENT_PROMPT_ASSETS.has(promptName)
);
}
export function assertNativeAgentCanonicalTargets(
manifest: Pick<CatalogManifest, "agents">,
): void {
const byName = getCatalogAgentByName(manifest);
for (const agent of manifest.agents) {
if (agent.status !== "alias" && agent.status !== "merged") continue;
if (!agent.canonical) {
throw new Error(
[
"native_agent_canonical_invalid",
`agent=${agent.name}`,
"message=alias/merged native agents must declare a canonical target",
].join("\n"),
);
}
const canonical = byName.get(agent.canonical);
if (!canonical) {
throw new Error(
[
"native_agent_canonical_invalid",
`agent=${agent.name}`,
`canonical=${agent.canonical}`,
"message=canonical native agent target is not listed in the catalog",
].join("\n"),
);View on GitHub (pinned to 3ad79a8a6f)
Solutions
- Re-download the archive and verify checksum/size against the release metadata
- Confirm with unzip -l / tar -tvf that the entry size is non-zero; if not, the upstream asset is broken — rebuild it
- Clear any intermediary cache (npm cache, CI cache, mirror) that may hold the truncated copy
- Audit the release pipeline so binaries are fully built before archiving
Example fix
// before await writeSelectedNativeArchiveMember(archive, 'mytool', dest); // throws archive_binary_empty // after const entries = await inspectNativeArchive(archive); const e = entries.find(c => c.normalizedName === 'mytool'); if (!e || e.size <= 0) await refetchArchive(); await writeSelectedNativeArchiveMember(archive, 'mytool', dest);
Defensive patterns
Strategy: validation
Validate before calling
const entries = await inspectNativeArchive(archivePath);
const e = entries.find(c => c.type === 'file' && c.normalizedName === member);
if (!e || e.size <= 0) throw new Error('refusing to hydrate empty binary member'); Type guard
const isHydratable = (e: { type: string; size: number }): boolean => e.type === 'file' && e.size > 0; Try / catch
try { await writeSelectedNativeArchiveMember(p, m, d); } catch (e) { if ((e as {code?:string}).code === 'archive_binary_empty') { await rm(p, {force:true}); /* re-fetch asset, retry once */ } else throw e; } Prevention
- Pre-check entry size before writing to disk
- Verify checksums after download
- Bust caches when a zero-byte entry is detected
When it happens
Trigger: writeSelectedNativeArchiveMember found the entry but selected.size <= 0 at the pre-write check. Same root causes as other empty-entry errors: truncated archive, placeholder file, or corrupt size header.
Common situations: Disk-full during CI artifact packaging producing empty entries; partially-synced mirrors or caches; LFS pointers not fetched; antivirus/quarantine tools zeroing out binaries inside archives on shared storage.
Related errors
- Unknown adapt target: ${target}
- archive_inspection_failed
- archive_format_unsupported
- invalid auth slot name: use 1-64 letters, numbers, '.', '_'
- (result.stderr || '').trim() || `git status failed for ${wor
AI-assisted analysis of Yeachan-Heo/oh-my-codex@3ad79a8a6f (2026-08-27).
Data as JSON: /api/errors/3431107d24eb0c82.
Report an issue: GitHub.