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

  1. Check the raw API response and align the Photon client version with the running server (response-shape drift)
  2. Update mocks/test doubles to return { guid, chatGuids: [chatGuid] } shaped receipts
  3. Verify the chatGuid you send with matches the chat the server actually delivered to (merge/renumber cases)
  4. 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

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


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)