paperclipai/paperclip · warning · Error
paperclip_runner_attachment_staging_cleanup_failed
paperclip_runner_attachment_staging_cleanup_failed
Error message
paperclip_runner_attachment_staging_cleanup_failed
What it means
Raised by the cleanup function returned from stageNativeRunnerWakeAttachments when one or more per-attachment cleanup callbacks reject after Promise.allSettled. It signals that staged attachment files could not be fully removed from the workspace staging directory, leaving residue behind.
Solutions
- Retry cleanup() after a short delay to let file locks release
- Manually remove leftover files under the workspace's private staging directory (the per-process staging subdirectory)
- Check filesystem permissions and that the workspace root still exists before rerunning the agent
- Inspect logs to identify which attachment cleanup rejected and fix the underlying cause (lock, permission, missing path)
Example fix
// before
await staged.cleanup(); // throws cleanup_failed, residue left
// after
try {
await staged.cleanup();
} catch (err) {
if (String(err).includes("paperclip_runner_attachment_staging_cleanup_failed")) {
await new Promise((r) => setTimeout(r, 250));
await staged.cleanup().catch(() => log.warn("staging residue remains; scrub on next run"));
} else throw err;
} Defensive patterns
Strategy: try-catch
Validate before calling
null
Type guard
null
Try / catch
try {
await staged.cleanup();
} catch (err) {
if (err instanceof Error && err.message === "paperclip_runner_attachment_staging_cleanup_failed") {
// schedule retry; residue is scrubbed automatically on the next staging run in this workspace
} else throw err;
} Prevention
- Always call cleanup() in a finally block after consuming staged attachments
- Avoid holding staged files open (editors, watchers) during the run
- Keep the workspace root writable for the staging process
- Rely on the staging registry scrub to clean residue from prior failed cleanups
When it happens
Trigger: Calling the returned cleanup() after staging completes while at least one writeStagedAttachment cleanup fails (e.g. unlink fails because the file is locked, permissions changed, or the workspace directory was removed mid-flight).
Common situations: Antivirus or a file watcher holding the staged file open on Windows; workspace directory deleted while the run was executing; read-only filesystem or permission change on the staging directory; concurrent scrub racing with cleanup.
Understand the failure class
Background: "failed to write file", "Could not save figure", "Error saving remote file" — file write failed: causes and fixes across languages and libraries — this error's family across 38 libraries.
Related errors
- Could not clean managed GitHub launchers
- paperclip_runner_attachment_staging_count_denied
- paperclip_runner_chat_attachment_read_cleanup_failed
- Workspace file must have a canonical absolute path
- Workspace uploads require a platform with confined file…
AI-assisted analysis of paperclipai/paperclip@3f1d897a7c (2026-09-18).
Data as JSON: /api/errors/41d445f7d613ba60.
Report an issue: GitHub.
Appendix: source
Thrown at server/src/services/native-runtime/native-runner-file-handoff.ts:1043
staged.push({
id: row.id,
filename: row.filename?.trim() || "attachment",
contentType: row.contentType,
byteSize: row.byteSize,
workspaceRelativePath: null,
unavailableReason: "attachment_staging_failed",
});
}
}
return {
attachments: staged,
cleanup: async () => {
try {
const results = await Promise.allSettled(
cleanups.map((cleanup) => cleanup()),
);
if (results.some((result) => result.status === "rejected")) {
throw new Error(
"paperclip_runner_attachment_staging_cleanup_failed",
);
}
} finally {
releaseActiveStage();
}
},
};
} catch (error) {
releaseActiveStage();
throw error;
}
}
export function renderNativeRunnerStagedAttachmentPrompt(
attachments: readonly NativeRunnerStagedAttachment[],
): string {
if (attachments.length === 0) return "";View on GitHub (pinned to 3f1d897a7c)