{"record":{"id":"dcba5fa814a9edfd","repo":"paperclipai/paperclip","slug":"photon-returned-an-invalid-send-receipt","errorCode":null,"errorMessage":"Photon returned an invalid send receipt","messagePattern":"Photon returned an invalid send receipt","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"server/src/services/photon/adapter.ts","lineNumber":566,"sourceCode":"        ? await this.client.messages.sendAttachment(\n            payload.chatGuid,\n            record.attachmentGuid,\n            { clientMessageId, replyTo: payload.replyTo },\n          )\n        : payload.replaceMessageId\n          ? await this.client.messages.edit(\n              payload.chatGuid,\n              payload.replaceMessageId,\n              payload.text!,\n              { clientMessageId },\n            )\n          : await this.client.messages.sendText(\n              payload.chatGuid,\n              payload.text!,\n              { clientMessageId, replyTo: payload.replyTo },\n            );\n      if (!result.guid || !result.chatGuids.includes(payload.chatGuid))\n        throw new Error(\"Photon returned an invalid send receipt\");\n      await save({ phase: \"sent\", messageGuid: result.guid });\n      return result.guid;\n    } catch (error) {\n      const failure = photonFailure(error, true);\n      if (failure.code !== \"delivery_unknown\")\n        await save({ phase: record.attachmentGuid ? \"uploaded\" : \"prepared\" });\n      throw failure;\n    }\n  }\n}\n","sourceCodeStart":548,"sourceCodeEnd":577,"githubUrl":"https://github.com/paperclipai/paperclip/blob/3f1d897a7c018d76563a21c6e39c3c9b03933622/server/src/services/photon/adapter.ts#L548-L577","documentation":"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'.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"// before\nconst result = await client.messages.sendText(chatGuid, text, {});\n// after\nconst result = await client.messages.sendText(chatGuid, text, {});\nif (!result?.guid || !result.chatGuids?.includes(chatGuid)) {\n  throw new Error(`invalid receipt: ${JSON.stringify(result)}`); // log for diagnosis\n}","handlingStrategy":"try-catch","validationCode":null,"typeGuard":"function isValidReceipt(result, chatGuid) {\n  return typeof result?.guid === 'string' && result.guid.length > 0 &&\n    Array.isArray(result.chatGuids) && result.chatGuids.includes(chatGuid);\n}","tryCatchPattern":"try {\n  await adapter.publish(id, pubId, msg, opts);\n} catch (e) {\n  if (e.message === 'Photon returned an invalid send receipt') {\n    log.error('invalid receipt', { publicationId: pubId, serverVersion: photonServerVersion });\n    return retryAfterContractCheck(); // record was rolled back to prepared/uploaded\n  }\n  throw e;\n}","preventionTips":["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"],"tags":["api-contract","unexpected-response","receipt-validation"],"backgroundTag":"unexpected-api-response-shape","analyzedSha":"3f1d897a7c018d76563a21c6e39c3c9b03933622","analyzedAt":"2026-09-18T08:03:59.046Z","contentChangedAt":"2026-09-18T08:03:59.046Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}