{"record":{"id":"05a7044a8911d9e6","repo":"paperclipai/paperclip","slug":"delivery-unknown-photon-poll-creation-is-reconcile-this","errorCode":"delivery_unknown","errorMessage":"Photon poll creation is unknown; reconcile this publication before retrying","messagePattern":"Photon poll creation is unknown; reconcile this publication before retrying","errorType":"error_code","errorClass":"PhotonError","httpStatus":null,"severity":"error","filePath":"server/src/services/photon/interactions.ts","lineNumber":283,"sourceCode":"    publicationId: binding.publicationId,\n    questionIndex,\n    promptMessageGuid: prompt.id,\n    promptMessageGuids: prompt.messageIds,\n    options: {},\n  };\n  if (nativePoll) {\n    const stateKey = `poll:${binding.publicationId}:${questionIndex}`;\n    type PollSend = {\n      schema: 1;\n      phase: \"prepared\" | \"creating\" | \"created\";\n      receipt?: {\n        pollMessageGuid: string;\n        options: Array<{ optionIdentifier: string; text: string }>;\n      };\n    };\n    let saved = await adapter.state.read<PollSend>(stateKey);\n    if (saved?.phase === \"creating\" && !input.retryUnknown)\n      throw new PhotonError(\n        \"delivery_unknown\",\n        \"Photon poll creation is unknown; reconcile this publication before retrying\",\n      );\n    if (!saved || saved.phase !== \"created\") {\n      await assertCurrent();\n      await adapter.state.update<PollSend>(stateKey, (current) => {\n        if (current?.phase === \"creating\" && !input.retryUnknown)\n          throw new PhotonAnswerValidationError(\"Photon poll already claimed\");\n        return { schema: 1, phase: \"creating\" };\n      });\n      const poll = await adapter.client.polls\n        .create(\n          adapter.decodeThreadId(input.threadId).chatGuid,\n          title,\n          choices.map((choice) => choice.label),\n          {\n            clientMessageId: createHash(\"sha256\")\n              .update(`${adapter.state.scope.endpointId}:${stateKey}`)","sourceCodeStart":265,"sourceCodeEnd":301,"githubUrl":"https://github.com/paperclipai/paperclip/blob/3f1d897a7c018d76563a21c6e39c3c9b03933622/server/src/services/photon/interactions.ts#L265-L301","documentation":"publishPhotonPrompt persists a per-poll state record (`poll:<publicationId>:<questionIndex>`) with phases prepared -> creating -> created. If a previous attempt recorded phase \"creating\" (meaning a polls.create call was dispatched but its outcome is unknown) and the caller has not opted into retryUnknown, the function refuses to blindly re-send and throws this PhotonError with code delivery_unknown. It exists to prevent duplicate poll delivery when the outcome of the prior Photon API call was never confirmed.","triggerScenarios":"Calling publishPhotonPrompt (via first/second/receipt paths) when adapter.state.read(stateKey) returns a PollSend with phase \"creating\" — i.e. a prior create attempt crashed, timed out, or threw an opaque network error after marking creating — and input.retryUnknown is falsy.","commonSituations":"Process restart or crash between marking phase creating and receiving the polls.create response; an unhandled delivery_unknown from a prior run; a retry driver that does not pass retryUnknown after an unknown-outcome failure; stale state left from an earlier deployment of the same publicationId.","solutions":["Run the reconciliation flow for this publication (confirm whether the poll actually exists in Photon), then either advance state to created/prepared or clear it.","Retry the publication with input.retryUnknown set to true once you have determined re-sending is safe (the clientMessageId hash dedupes on the Photon side).","Inspect adapter state for key `poll:<publicationId>:<questionIndex>` and manually reset it to {schema:1, phase:\"prepared\"} if the poll was never created.","If the create call is known to have failed with a definitive (non-unknown) error, the catch handler should have reset the phase; investigate why state still says creating."],"exampleFix":"// before\nawait publishPhotonPrompt({ threadId, retryUnknown: false, ... });\n// after — after reconciling that the poll was not delivered\nawait publishPhotonPrompt({ threadId, retryUnknown: true, ... });","handlingStrategy":"retry","validationCode":"const stateKey = `poll:${publicationId}:${questionIndex}`;\nconst saved = await adapter.state.read<{phase:string}>(stateKey);\nconst safeToPublish = !saved || saved.phase === \"prepared\" || saved.phase === \"created\" || retryUnknown;","typeGuard":"function isResumablePollState(s: {phase: string} | null | undefined, retryUnknown: boolean): boolean {\n  return !s || s.phase === \"prepared\" || s.phase === \"created\" || retryUnknown;\n}","tryCatchPattern":"try {\n  await publishPhotonPrompt(input);\n} catch (e) {\n  if (e instanceof PhotonError && e.code === \"delivery_unknown\") {\n    await reconcilePublication(input.publicationId); // confirm with Photon, reset state\n    await publishPhotonPrompt({ ...input, retryUnknown: true });\n  } else throw e;\n}","preventionTips":["After any unknown-outcome create failure, run reconciliation before the next attempt.","Always pass the same clientMessageId so Photon-side dedupe protects retries.","Serialize publishing per publicationId to avoid racing state transitions.","Monitor for state records stuck in phase creating and alert on them."],"tags":["photon","state-machine","idempotency","poll-delivery"],"backgroundTag":"invalid-state-transition","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"}