{"record":{"id":"f27c2cad6e01ad9e","repo":"paperclipai/paperclip","slug":"photon-cannot-edit-a-multipart-publication","errorCode":null,"errorMessage":"Photon cannot edit a multipart publication","messagePattern":"Photon cannot edit a multipart publication","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"server/src/services/photon/adapter.ts","lineNumber":421,"sourceCode":"    publicationId: string,\n    message: AdapterPostableMessage,\n    options: {\n      replyTo?: string;\n      replaceMessageId?: string;\n      retryUnknown?: boolean;\n      assertCurrent(): Promise<void>;\n    },\n  ): Promise<{ id: string; messageIds: string[] }> {\n    const chat = await this.chatInfo(id);\n    if (chat.isArchived) throw new Error(\"Photon conversation is unavailable\");\n    const text = textOf(message);\n    const files =\n      typeof message !== \"string\" && \"files\" in message\n        ? (message.files ?? [])\n        : [];\n    const parts = splitPhotonText(text);\n    if (options.replaceMessageId && (parts.length !== 1 || files.length))\n      throw new Error(\"Photon cannot edit a multipart publication\");\n    let lastId: string | undefined;\n    const messageIds: string[] = [];\n    for (let index = 0; index < parts.length; index++) {\n      lastId = await this.sendPart(\n        publicationId,\n        `text-${index}`,\n        {\n          chatGuid: chat.guid,\n          text: parts[index],\n          replyTo: options.replyTo,\n          replaceMessageId: options.replaceMessageId,\n        },\n        undefined,\n        options,\n      );\n      messageIds.push(lastId);\n    }\n    for (let index = 0; index < files.length; index++) {","sourceCodeStart":403,"sourceCodeEnd":439,"githubUrl":"https://github.com/paperclipai/paperclip/blob/3f1d897a7c018d76563a21c6e39c3c9b03933622/server/src/services/photon/adapter.ts#L403-L439","documentation":"Thrown in PhotonAdapter.publish when an edit (replaceMessageId set) would replace a message that splits into multiple text parts or carries attachments. iMessage edits can only change a single text bubble, so the adapter refuses multipart/attachment publications rather than producing an edit that cannot round-trip.","triggerScenarios":"publish(id, publicationId, message, { replaceMessageId, ... }) where splitPhotonText(textOf(message)) yields parts.length !== 1, or message.files is a non-empty array.","commonSituations":"Editing a long agent response that exceeds the Photon text-part limit and was split into multiple bubbles; adding files to what started as an edit; reusing a replace flow for a publication that grew over size thresholds.","solutions":["Ensure the replacement text fits in a single Photon text part (shorten the message below the split threshold)","Send the correction as a brand-new publication without replaceMessageId instead of an edit","Remove files from the edited message and publish attachments separately as a new publication","Adjust the UI/flow so edits are only offered for single-part, text-only messages"],"exampleFix":"// before\nawait adapter.publish(id, pubId, { text: longText }, { replaceMessageId: msgId, ... });\n// after\nconst parts = splitPhotonText(longText);\nif (parts.length !== 1) {\n  await adapter.publish(id, pubId, { text: longText }, opts); // new publication\n} else {\n  await adapter.publish(id, pubId, { text: longText }, { ...opts, replaceMessageId: msgId });\n}","handlingStrategy":"validation","validationCode":"const parts = splitPhotonText(textOf(message));\nif (opts.replaceMessageId && (parts.length !== 1 || message.files?.length))\n  throw new Error('Edits must be single-part text-only; publish as new publication instead');","typeGuard":"function isEditable(msg, replaceId) {\n  return Boolean(replaceId) && splitPhotonText(textOf(msg)).length === 1 && !(msg.files?.length);\n}","tryCatchPattern":"try {\n  await adapter.publish(id, pubId, msg, opts);\n} catch (e) {\n  if (e.message === 'Photon cannot edit a multipart publication') {\n    delete opts.replaceMessageId;\n    return adapter.publish(id, pubId, msg, opts); // new publication\n  }\n  throw e;\n}","preventionTips":["Only offer edit UI for messages that fit one Photon text part and have no attachments","Split or truncate long text before planning an edit","Never attach files to a replacement publication","Centralize edit eligibility checks in one helper"],"tags":["imessage","edit","multipart","unsupported-operation"],"backgroundTag":"unsupported-operation","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"}