JuliusBrussee/caveman · error

${skill.file} failed skill postflight

Error message

${skill.file} failed skill postflight

What it means

Postflight loop in verifyAgentNativeBundle: every skill file recorded in the AgentNativeBundleJournal must still exist on disk (fileBytes returns bytes) and hash to the after_sha256 captured at write time. A missing file or content change means the skill bundle did not survive setup or was modified immediately after.

Source

Thrown at packages/cli/src/index.ts:2596

      before_base64: originals.get(file) ?? null,
      after_sha256: bytesHash(Buffer.from(body)),
    };
  });
}

function verifyAgentNativeBundle(agent: "claude" | "codex", journal: AgentNativeBundleJournal, cloudMcp: { command: string; args: string[] }): void {
  const status = nativeIntegrationStatus(agent);
  if (status.state !== "installed") throw new Error(`${agent} native integration postflight is ${status.state}`);
  const marker = readMcpServerMarker(agent, "caveman-cloud");
  if (!marker || marker.command !== cloudMcp.command || JSON.stringify(marker.args) !== JSON.stringify(cloudMcp.args)) {
    throw new Error(`${agent} caveman-cloud MCP postflight mismatch`);
  }
  if (!agentNativeCloudMcpMatches(agent, cloudMcp)) {
    throw new Error(`${agent} caveman-cloud MCP registration failed exact postflight`);
  }
  for (const skill of journal.skills) {
    const current = fileBytes(skill.file);
    if (!current || bytesHash(current) !== skill.after_sha256) throw new Error(`${skill.file} failed skill postflight`);
  }
}

function removeAgentNativeBundle(agent: "claude" | "codex"): void {
  recoverPendingAgentNativeRemoval(agent);
  recoverPendingAgentNativeBundle(agent);
  const journal = readAgentNativeBundleJournal(agent);
  if (!journal) {
    process.stderr.write(`${mark("warn")} ${agent}: no agent-native bundle journal found\n`);
    return;
  }
  for (const skill of journal.skills) {
    const current = fileBytes(skill.file);
    if (!current || bytesHash(current) !== skill.after_sha256) throw new Error(`${skill.file} changed after setup; refusing destructive bundle removal`);
  }
  const marker = readMcpServerMarker(agent, "caveman-cloud");
  if (!sameMcpCommand(marker, journal.cloud_mcp) || !agentNativeCloudMcpMatches(agent, journal.cloud_mcp)) {
    throw new Error(`${agent} caveman-cloud MCP changed after setup; refusing destructive bundle removal`);

View on GitHub (pinned to 27d5a3981a)

Solutions

  1. Free disk space and re-run `caveman setup --agent-native <agent> --remove` followed by setup
  2. Temporarily disable editors/formatters/cloud-sync watching the agent skills directory during setup
  3. Verify permissions on the agent skills directory and repair with a clean reinstall of the bundle
Defensive patterns

Strategy: try-catch

Validate before calling

// pre-flight: writable skills dir + disk headroom avoids truncated writes
import { accessSync, constants } from "node:fs";
accessSync(skillsDir, constants.W_OK);
const stat = await import("node:fs/promises").then((f) => f.statfs(skillsDir));
if (Number(stat.bavail) * Number(stat.bsize) < 50 * 1024 * 1024) throw new Error("<50MB free; skill writes may truncate");

Try / catch

try {
  await setup(["--agent-native", agent]);
} catch (error) {
  if (/failed skill postflight/.test((error as Error).message)) {
    // file named in the message drifted; clean reinstall is the reliable repair
    await setup(["--agent-native", agent, "--remove"]).catch(() => {});
    await setup(["--agent-native", agent]);
  } else throw error;
}

Prevention

When it happens

Trigger: Running agent-native setup when a skill file is deleted between write and verification, or when antivirus/editor/another process rewrites the file; also when after_sha256 was journaled from a different body than what was written (partial write, disk full).

Common situations: Disk-full during setup leaving truncated skill files; file watchers (formatters, sync clients) touching freshly written SKILL.md files; two concurrent setups writing overlapping skill paths.

Related errors


AI-assisted analysis of JuliusBrussee/caveman@27d5a3981a (2026-08-15). Data as JSON: /api/errors/75f190639ed9fd92. Report an issue: GitHub.