paperclipai/paperclip · warning · Error
Attachment was removed
Error message
Attachment was removed
What it means
In TaskChatComposer's `uploadInlineImage`, after the async upload resolves it re-checks that the placeholder attachment (tracked by `id`) still exists in `attachmentsRef.current`. If the user removed the attachment while the upload was in flight, the composer throws instead of mutating state for a deleted item.
Solutions
- Expected and harmless: catch it in the caller and ignore (attachment intentionally removed) — ensure the catch is silent for this case.
- UX fix: disable the per-attachment remove button while `item.uploading` is true.
- Alternative: re-add nothing but return early instead of throwing when the id is absent.
Example fix
// before
if (!attachmentsRef.current.some((item) => item.id === id))
throw new Error("Attachment was removed");
// after
if (!attachmentsRef.current.some((item) => item.id === id))
return; // user removed it mid-upload; nothing to update Defensive patterns
Strategy: try-catch
Validate before calling
if (!attachmentsRef.current.some((i) => i.id === id)) return; // still attached?
Type guard
const stillAttached = (id: string) => attachmentsRef.current.some((item) => item.id === id);
Try / catch
try { await uploadInlineImage(file); } catch (e) { if (!/Attachment was removed/.test(e.message)) showToast('Upload failed'); } Prevention
- Treat mid-upload removal as an expected no-op, not an error.
- Disable remove buttons while an attachment is uploading.
- Keep upload results keyed by id and ignore results for deleted ids.
When it happens
Trigger: Start an inline image upload, then click remove on that attachment before the upload completes; the resolved upload then finds the id gone from `attachmentsRef.current`.
Common situations: Slow uploads on poor networks combined with quick user edits; user hits a 'remove all attachments' action mid-upload; rapid delete/undo interactions.
Understand the failure class
Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.
Related errors
- ACPX runtime executable changed while it was verified
- ACPX executable changed during snapshot
- ACPX file ended during snapshot
- ACPX module changed before snapshot
- ACPX module changed during snapshot
AI-assisted analysis of paperclipai/paperclip@3f1d897a7c (2026-09-18).
Data as JSON: /api/errors/287eb6fcd8e12fe1.
Report an issue: GitHub.
Appendix: source
Thrown at ui/src/components/task-chat/TaskChatComposer.tsx:685
const id = crypto.randomUUID();
setAttachments((prev) => [
...prev,
{
id,
name: file.name,
size: file.size,
inline: true,
status: "uploading",
},
]);
try {
const attachment = onAttachImage ? await onAttachImage(file) : undefined;
const url = onAttachImage
? attachment?.contentPath
: await onImageUpload?.(file);
if (!url) throw new Error("Upload did not return a file URL");
if (!attachmentsRef.current.some((item) => item.id === id))
throw new Error("Attachment was removed");
setAttachments((prev) =>
prev.map((item) =>
item.id === id
? {
...item,
attachmentId: attachment?.id,
contentPath: url,
status: "attached",
}
: item,
),
);
return url;
} catch (err) {
setAttachments((prev) =>
prev.map((item) =>
item.id === id
? {View on GitHub (pinned to 3f1d897a7c)