paperclipai/paperclip · warning · PhotonError
attachment_not_ready
attachment_not_ready
Error message
Photon attachment is still being prepared; retry download
What it means
After the stream ends, the code checks for timeout, presence of a header, and at least one byte of primary data. If the 30s timer fired, no header arrived, or zero bytes were received, it throws a PhotonError with code attachment_not_ready (server/src/services/photon/attachments.ts:173). This signals the attachment is not yet downloadable (still being prepared server-side) rather than a hard failure.
Solutions
- Retry the download after a short backoff (e.g. 1-5s, a few attempts) — this error is explicitly retryable.
- Delay first download attempt for very fresh messages to let Photon finish preparing the attachment.
- Increase tolerance for slow gateways (the 30s timeout is fixed in this function; schedule retries around it).
- Check gateway health/connectivity if every attempt times out with zero bytes.
Example fix
// before
const body = await downloadPhotonAttachment(client, lineId, locator); // throws when not ready
// after
let body;
for (let attempt = 0; attempt < 4; attempt++) {
try { body = await downloadPhotonAttachment(client, lineId, locator); break; }
catch (e) {
if (e instanceof PhotonError && e.code === "attachment_not_ready") {
await new Promise(r => setTimeout(r, 2000 * (attempt + 1)));
continue;
}
throw e;
}
} Defensive patterns
Strategy: retry
Try / catch
try {
await downloadPhotonAttachment(client, lineId, locator);
} catch (e) {
if (e instanceof PhotonError && e.code === "attachment_not_ready") {
await sleep(backoffMs); // exponential, bounded attempts
return retryDownload(locator);
}
throw e;
} Prevention
- Wait briefly after new-message events before first download attempt
- Implement exponential backoff with a bounded attempt count for attachment_not_ready
- Monitor gateway latency; frequent timeouts indicate infrastructure, not attachment, issues
When it happens
Trigger: stream.close() fired by the 30s timeout before data completed; stream ended with no header part; stream ended with length === 0.
Common situations: Recipient just sent a large photo and Photon has not finished ingesting it; gateway is slow or cold-starting; network stall triggering the 30s timeout; immediately reacting to a new-message event before the attachment is ready.
Understand the failure class
Background: Request timed out: what client-side request timeouts mean across libraries (Request timed out, TIMED_OUT, APITimeoutError) — this error's family across 39 libraries.
Related errors
- github_attachment_download_failed
- network
- --agent-name cannot be empty.
- --api-key and --api-key-env are mutually exclusive.
- Attachment exceeds the configured size limit
AI-assisted analysis of paperclipai/paperclip@3f1d897a7c (2026-09-18).
Data as JSON: /api/errors/9be014b2bbd74f51.
Report an issue: GitHub.
Appendix: source
Thrown at server/src/services/photon/attachments.ts:173
if (!header) throw new Error("Photon attachment header is missing");
length += part.data.length;
if (length > MAX_ATTACHMENT_BYTES)
throw new Error("Attachment exceeds the configured size limit");
chunks.push(part.data);
} else if (part.type === "companionChunk") {
if (!header || !companionInfo)
throw new Error("Photon companion metadata is missing");
companionStarted = true;
companionLength += part.data.length;
if (companionUnavailable || companionLength > MAX_ATTACHMENT_BYTES) {
companionUnavailable = true;
break;
}
companionChunks.push(part.data);
}
}
if (timedOut || !header || !length)
throw new PhotonError(
"attachment_not_ready",
"Photon attachment is still being prepared; retry download",
);
if (attachment.totalBytes > 0 && length !== attachment.totalBytes)
throw new PhotonError(
"attachment_not_ready",
"Photon attachment transfer is incomplete",
);
if (
companionInfo &&
!companionUnavailable &&
companionLength !== companionInfo.totalBytes
)
throw new PhotonError(
"attachment_not_ready",
"Photon Live Photo companion is still being prepared",
);
const body = Buffer.concat(chunks);View on GitHub (pinned to 3f1d897a7c)