{"record":{"id":"96608b9bcd7d59ac","repo":"paperclipai/paperclip","slug":"photon-publication-is-empty","errorCode":null,"errorMessage":"Photon publication is empty","messagePattern":"Photon publication is empty","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"server/src/services/photon/adapter.ts","lineNumber":461,"sourceCode":"          ? Buffer.from(await file.data.arrayBuffer())\n          : Buffer.from(file.data as ArrayBuffer);\n      if (bytes.length > MAX_ATTACHMENT_BYTES)\n        throw new Error(\"Attachment exceeds the configured size limit\");\n      lastId = await this.sendPart(\n        publicationId,\n        `file-${index}`,\n        {\n          chatGuid: chat.guid,\n          filename: file.filename,\n          sha256: createHash(\"sha256\").update(bytes).digest(\"hex\"),\n          replyTo: options.replyTo,\n        },\n        { ...file, data: bytes },\n        options,\n      );\n      messageIds.push(lastId);\n    }\n    if (!lastId) throw new Error(\"Photon publication is empty\");\n    await this.endTyping(id);\n    return { id: lastId, messageIds };\n  }\n  private async sendPart(\n    publicationId: string,\n    part: string,\n    payload: {\n      chatGuid: string;\n      text?: string;\n      filename?: string;\n      sha256?: string;\n      replyTo?: string;\n      replaceMessageId?: string;\n    },\n    file: FileUpload | undefined,\n    options: { retryUnknown?: boolean; assertCurrent(): Promise<void> },\n  ): Promise<string> {\n    const key = `send:${publicationId}:${part}`;","sourceCodeStart":443,"sourceCodeEnd":479,"githubUrl":"https://github.com/paperclipai/paperclip/blob/3f1d897a7c018d76563a21c6e39c3c9b03933622/server/src/services/photon/adapter.ts#L443-L479","documentation":"Thrown at the end of PhotonAdapter.publish when no part was ever sent, i.e. lastId remained undefined after the text-parts and file loops. This happens when splitPhotonText produced zero parts and there were no files — the publication would have no content at all, so the adapter aborts instead of returning an empty receipt.","triggerScenarios":"publish() with an empty/whitespace-only text that splitPhotonText reduces to zero parts and an empty files array.","commonSituations":"An upstream agent produced an empty response; a message body was stripped to nothing by formatting/sanitization; a code path passed `undefined`/empty string as the message text while forgetting to include attachments.","solutions":["Guard the caller: check that textOf(message) is non-empty (or files non-empty) before calling publish","Fix the upstream step that produced the empty response instead of publishing an empty publication","Coalesce empty publications into a placeholder ('(no output)') only if the product allows it"],"exampleFix":"// before\nawait adapter.publish(id, pubId, emptyMessage, opts);\n// after\nconst text = textOf(message);\nif (!text.trim() && !(message.files?.length)) {\n  throw new EmptyPublicationError();\n}\nawait adapter.publish(id, pubId, message, opts);","handlingStrategy":"validation","validationCode":"const text = textOf(message);\nif (!text?.trim() && !(message.files?.length))\n  throw new Error('Refusing to publish an empty publication');","typeGuard":"function isNonEmptyPublication(msg) {\n  return Boolean(textOf(msg)?.trim()) || Boolean(msg.files?.length);\n}","tryCatchPattern":"try {\n  await adapter.publish(id, pubId, msg, opts);\n} catch (e) {\n  if (e.message === 'Photon publication is empty') {\n    log.error('empty publication', { publicationId: pubId });\n    return null; // nothing to deliver\n  }\n  throw e;\n}","preventionTips":["Validate non-empty text or files at the call site","Guard upstream agent steps against producing empty output","Be careful with sanitizers/transformations that can strip all content","Add a unit test for empty-message publish behavior"],"tags":["empty-content","validation","imessage"],"backgroundTag":"empty-required-field","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"}