{"record":{"id":"25fee57c00e4637b","repo":"paperclipai/paperclip","slug":"photon-sends-require-an-immutable-paperclip-publication","errorCode":null,"errorMessage":"Photon sends require an immutable Paperclip publication identity","messagePattern":"Photon sends require an immutable Paperclip publication identity","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"server/src/services/photon/adapter.ts","lineNumber":378,"sourceCode":"      this.typing.delete(id);\n      void this.refreshTyping(id, true).catch(() => {});\n    }, 8_000);\n    timer.unref();\n    this.typing.set(id, timer);\n  }\n  async endTyping(id: string): Promise<void> {\n    if (this.typing.has(id)) clearTimeout(this.typing.get(id));\n    this.typing.delete(id);\n    if (!this.closed)\n      await this.client.chats\n        .setTyping(this.decodeThreadId(id).chatGuid, false)\n        .catch(() => {});\n  }\n  async postMessage(\n    _id: string,\n    _message: AdapterPostableMessage,\n  ): Promise<never> {\n    throw new Error(\n      \"Photon sends require an immutable Paperclip publication identity\",\n    );\n  }\n  async editMessage(\n    _id: string,\n    _messageId: string,\n    _message: AdapterPostableMessage,\n  ): Promise<never> {\n    throw new Error(\n      \"Photon edits require an immutable Paperclip publication identity\",\n    );\n  }\n  async deleteMessage(): Promise<never> {\n    throw new Error(\"Photon message deletion is not supported\");\n  }\n  async addReaction(): Promise<void> {\n    /* Receipt reactions are intentionally not published. */\n  }","sourceCodeStart":360,"sourceCodeEnd":396,"githubUrl":"https://github.com/paperclipai/paperclip/blob/3f1d897a7c018d76563a21c6e39c3c9b03933622/server/src/services/photon/adapter.ts#L360-L396","documentation":"Photon's postMessage is deliberately unimplemented: it always throws 'Photon sends require an immutable Paperclip publication identity'. Photon messages published through Paperclip need a stable publication identity, and the adapter refuses ad-hoc sends that would break that immutability guarantee.","triggerScenarios":"Any call to adapter.postMessage(chatId, message) on the Photon adapter, regardless of arguments.","commonSituations":"Generic notification or bot code that calls postMessage on every messaging adapter without excluding Photon; attempting to send agent replies through the Photon channel.","solutions":["Do not call postMessage on the Photon adapter; treat Photon as read/receipt-only for outbound publication.","Route outbound messages through the Paperclip publication-identity path instead of direct adapter sends.","Feature-check before sending: skip or no-op Photon when the code path requires postMessage."],"exampleFix":"// before\nawait adapter.postMessage(chatId, message);\n// after\nif (typeof adapter.postMessage === \"function\" && adapter.capabilities?.canPost) {\n  await adapter.postMessage(chatId, message);\n}","handlingStrategy":"type-guard","validationCode":"if (adapter === photonAdapter || !adapter.capabilities?.canPost) return; // Photon cannot post","typeGuard":"const canPost = (a: unknown): a is AdapterWithPost => typeof (a as AdapterWithPost).postMessage === \"function\" && (a as AdapterWithPost).capabilities?.canPost === true;","tryCatchPattern":"try {\n  await adapter.postMessage(chatId, msg);\n} catch (e) {\n  if (e.message.includes(\"immutable Paperclip publication identity\")) return; // Photon: sends unsupported\n  throw e;\n}","preventionTips":["Maintain an explicit per-adapter capability map (post/edit/delete) and consult it before sends.","Exclude Photon from generic outbound-notification loops.","Use the Paperclip publication-identity path for any Photon-bound outbound content."],"tags":["photon","messaging","unsupported-operation"],"backgroundTag":"method-not-implemented","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"}