paperclipai/paperclip · error
Photon returned an invalid send receipt
Error message
Photon returned an invalid send receipt
What it means
Thrown in sendPart after a successful-looking sendText/sendAttachment/edit response when the receipt is unusable: result.guid is falsy or result.chatGuids does not include the target chatGuid. The adapter persists phase 'sent' only with a valid guid scoped to the right chat, so a malformed receipt aborts and (for non-delivery_unknown failures) the record is rolled back to 'uploaded'/'prepared'.
Solutions
- Check the raw API response and align the Photon client version with the running server (response-shape drift)
- Update mocks/test doubles to return { guid, chatGuids: [chatGuid] } shaped receipts
- Verify the chatGuid you send with matches the chat the server actually delivered to (merge/renumber cases)
- Since the record is rolled back to prepared/uploaded, simply retrying publish after fixing the root cause is safe
Example fix
// before
const result = await client.messages.sendText(chatGuid, text, {});
// after
const result = await client.messages.sendText(chatGuid, text, {});
if (!result?.guid || !result.chatGuids?.includes(chatGuid)) {
throw new Error(`invalid receipt: ${JSON.stringify(result)}`); // log for diagnosis
} Defensive patterns
Strategy: try-catch
Type guard
function isValidReceipt(result, chatGuid) {
return typeof result?.guid === 'string' && result.guid.length > 0 &&
Array.isArray(result.chatGuids) && result.chatGuids.includes(chatGuid);
} Try / catch
try {
await adapter.publish(id, pubId, msg, opts);
} catch (e) {
if (e.message === 'Photon returned an invalid send receipt') {
log.error('invalid receipt', { publicationId: pubId, serverVersion: photonServerVersion });
return retryAfterContractCheck(); // record was rolled back to prepared/uploaded
}
throw e;
} Prevention
- Keep the Photon client SDK version aligned with the server (receipt schema drift)
- Add contract tests validating { guid, chatGuids } on all send paths
- Make test doubles return complete receipts
- Log the full response when receipt validation fails to catch shape changes early
When it happens
Trigger: Photon messages.sendText/sendAttachment/edit resolving with a response lacking guid, or with chatGuids that exclude payload.chatGuid — usually an API/SDK version drift or a proxied/mock response with a partial body.
Common situations: Photon server upgrade changing the receipt schema; SDK deserialization dropping fields; a test double returning an incomplete message object; message delivered to a merged/renamed chat so chatGuids differ from the requested guid.
Understand the failure class
Background: "invalid response format", "malformed payload", "missing data field": when an API returns 200 but the response shape is wrong — this error's family across 23 libraries.
Related errors
- cacheUserContext is unavailable
- Codex turn response omitted turn.id
- CreateOS file download has no body.
- CreateOS returned an unknown process event.
- decodeThreadId is unavailable
AI-assisted analysis of paperclipai/paperclip@3f1d897a7c (2026-09-18).
Data as JSON: /api/errors/dcba5fa814a9edfd.
Report an issue: GitHub.
Appendix: source
Thrown at server/src/services/photon/adapter.ts:566
? await this.client.messages.sendAttachment(
payload.chatGuid,
record.attachmentGuid,
{ clientMessageId, replyTo: payload.replyTo },
)
: payload.replaceMessageId
? await this.client.messages.edit(
payload.chatGuid,
payload.replaceMessageId,
payload.text!,
{ clientMessageId },
)
: await this.client.messages.sendText(
payload.chatGuid,
payload.text!,
{ clientMessageId, replyTo: payload.replyTo },
);
if (!result.guid || !result.chatGuids.includes(payload.chatGuid))
throw new Error("Photon returned an invalid send receipt");
await save({ phase: "sent", messageGuid: result.guid });
return result.guid;
} catch (error) {
const failure = photonFailure(error, true);
if (failure.code !== "delivery_unknown")
await save({ phase: record.attachmentGuid ? "uploaded" : "prepared" });
throw failure;
}
}
}
View on GitHub (pinned to 3f1d897a7c)