paperclipai/paperclip · error

Photon attachment header is missing

Error message

Photon attachment header is missing

What it means

Primary data chunks must only arrive after a header part that establishes metadata. If a primaryChunk arrives while header is still false, the stream had no header first, and this error is thrown (server/src/services/photon/attachments.ts:155).

Solutions

  1. Retry the download with a fresh stream; transient truncation is usually recovered by re-requesting.
  2. Verify the @photon-ai/advanced-imessage client and gateway are version-compatible (header frame support).
  3. Confirm the attachment still exists and is downloadable via a fresh client.messages.get before streaming.
  4. If persistent, treat the attachment as unavailable and surface a recoverable failure instead of retrying endlessly.
Defensive patterns

Strategy: retry

Try / catch

try {
  await downloadPhotonAttachment(client, lineId, locator);
} catch (e) {
  if (e instanceof Error && e.message === "Photon attachment header is missing") {
    // fresh-stream retry with small backoff; escalate if persistent
  } else throw e;
}

Prevention

When it happens

Trigger: The download stream yields part.type === "primaryChunk" before any part.type === "header" part.

Common situations: Gateway emitting truncated streams missing the header frame; protocol/version mismatch between client library and gateway; stream resumed mid-transfer where the header was dropped.

Related errors


AI-assisted analysis of paperclipai/paperclip@3f1d897a7c (2026-09-18). Data as JSON: /api/errors/20a5f9c6e17ea225. Report an issue: GitHub.

Appendix: source

Thrown at server/src/services/photon/attachments.ts:155

          part.info.totalBytes > MAX_ATTACHMENT_BYTES
        )
          throw new Error("Photon attachment metadata changed");
        header = true;
        companionInfo = part.companionInfo;
        companionUnavailable = Boolean(
          companionInfo &&
            (companionInfo.kind !== "live-photo-video" ||
              !["video/quicktime", "video/mp4"].includes(
                companionInfo.mimeType,
              ) ||
              !Number.isSafeInteger(companionInfo.totalBytes) ||
              companionInfo.totalBytes <= 0 ||
              companionInfo.totalBytes > MAX_ATTACHMENT_BYTES),
        );
      } else if (part.type === "primaryChunk") {
        if (companionStarted)
          throw new Error("Photon attachment chunks arrived out of order");
        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(

View on GitHub (pinned to 3f1d897a7c)