paperclipai/paperclip · error

Photon attachment chunks arrived out of order

Error message

Photon attachment chunks arrived out of order

What it means

During streaming, once companion (Live Photo video) chunks have started, no further primary image chunks are expected. If a primaryChunk arrives after companionStarted became true, the stream is malformed and this error is thrown (server/src/services/photon/attachments.ts:154). It protects against interleaved or corrupted multipart streams being concatenated into a wrong file.

Solutions

  1. Retry the download once; if transient, a fresh stream usually arrives in correct order.
  2. Check the @photon-ai/advanced-imessage / gateway version for known chunk-ordering bugs and upgrade.
  3. Skip Live Photo companion handling (or the attachment) if the source stream is persistently malformed.
  4. File a bug with the Photon gateway team including the messageGuid/attachmentGuid if reproducible.
Defensive patterns

Strategy: retry

Try / catch

try {
  await downloadPhotonAttachment(client, lineId, locator);
} catch (e) {
  if (e instanceof Error && e.message === "Photon attachment chunks arrived out of order") {
    // one fresh-stream retry, then treat the attachment as unavailable
  } else throw e;
}

Prevention

When it happens

Trigger: A Photon stream emits primaryChunk parts after companionChunk parts began, i.e. out-of-order interleaving in a live-photo transfer.

Common situations: Gateway/adapter bug emitting interleaved chunks for Live Photos; a stream replayed or multiplexed incorrectly; custom client implementation violating the Photon part ordering contract.

Related errors


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

Appendix: source

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

          !Number.isSafeInteger(part.info.totalBytes) || part.info.totalBytes < 0 ||
          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)

View on GitHub (pinned to 3f1d897a7c)