paperclipai/paperclip · error
Daytona post-upload command failed
Error message
Daytona post-upload command failed (exit ${result.exitCode ?? "unknown"})${detail ? `: ${detail}` : ""} What it means
Post-upload failure check in syncInFileMappings: a caller-supplied postUploadCommand run verbatim in the sandbox (after the cwd symlink-escape guard) exited non-zero or timed out, so the remaining queued commands are aborted per the C4 fail-fast rule. The detail includes the command's captured output.
Solutions
- Inspect the exit code and detail; fix the post-upload command and retry.
Defensive patterns
Strategy: validation
When it happens
Trigger: Thrown at packages/plugins/sandbox-providers/daytona/src/file-sync.ts:767 when the library encounters an invalid state.
Common situations: See trigger scenarios.
AI-assisted analysis of paperclipai/paperclip@3f1d897a7c (2026-08-18).
Data as JSON: /api/errors/5f81bf4c0f61070b.
Report an issue: GitHub.
Appendix: source
Thrown at packages/plugins/sandbox-providers/daytona/src/file-sync.ts:789
if (plan.compressed) {
// `zstd -d -o` copies the mode of its INPUT (the uploaded `.zst`
// scratch) onto its output with its own `chmod` call. That call runs
// AFTER creation, so it overrides any `umask` in effect — a mapping
// with no explicit `mode` must not rely on the scratch file's mode
// being owner-only already. Always `chmod` the decompressed file
// right after decompression: to the mapping's `mode` when set, or to
// owner-only (0600) otherwise. The pre-refactor decompression step
// applied the same 0600 default. The raw (uncompressed) path above
// applies no `chmod` when the mapping sets no `mode`, so the two
// inbound branches do not use the same no-mode default today.
const targetMode = typeof plan.mapping.mode === "number" ? plan.mapping.mode : 0o600;
renameScript.push(
`zstd -d -o ${shellQuote(plan.rawScratch)} ${shellQuote(plan.compressed.zstdScratch)} || { echo "decompress failed"; exit 49; };`,
`chmod ${toOctalModeString(targetMode)} ${shellQuote(plan.rawScratch)} || { echo "chmod failed"; exit 50; };`,
);
}
renameScript.push(
`mv -f ${shellQuote(plan.rawScratch)} ${shellQuote(plan.mapping.targetPath)} || { echo "rename failed"; exit 43; };`,
);
if (plan.compressed) {
// Clean up the `.zst` scratch after a successful promotion. `|| true`
// keeps a cleanup failure from becoming the promote script's own exit
// status — every target file is already in place by this point, so a
// stray `.zst` scratch must never read back as a sync failure.
renameScript.push(`rm -f ${shellQuote(plan.compressed.zstdScratch)} || true;`);
}
}
// `promote` span: move the staged temp onto its target. When this batch
// decompressed at least one mapping, this span also carries
// `transfer.decompress.wall_ms`. That value measures the WHOLE promote
// command — every decompression and every `mv` — not decompression alone.
// Treat it as an upper bound on the decompress wall time, not an exact
// measurement.
await withProviderSpan({
name: "promote",
wallMsAttr: hasCompressedMapping ? SPAN_ATTR.transferDecompressWallMs : undefined,View on GitHub (pinned to 3f1d897a7c)