{"record":{"id":"64a4e0654c1388ff","repo":"paperclipai/paperclip","slug":"durable-accepted-activity-recorder-is-unavailable","errorCode":null,"errorMessage":"durable accepted-activity recorder is unavailable","messagePattern":"durable accepted-activity recorder is unavailable","errorType":"exception","errorClass":"TeamsAdapterCompatibilityError","httpStatus":null,"severity":"error","filePath":"server/src/services/chat-sdk-runtime.ts","lineNumber":2554,"sourceCode":"   */\n  async recordMicrosoftTeamsRoute(\n    threadId: string,\n    serviceUrl: unknown,\n    raw?: unknown,\n  ): Promise<void> {\n    if (this.provider !== \"microsoft-teams\") return;\n    const teams = this.adapter as unknown as TeamsAdapterInternals;\n    const recorder = teams.paperclipRecordThreadServiceUrl;\n    if (typeof recorder !== \"function\") {\n      throw new TeamsAdapterCompatibilityError(\n        \"durable route recorder is unavailable\",\n      );\n    }\n    await recorder.call(this.adapter, threadId, serviceUrl);\n    if (raw !== undefined) {\n      const acceptedActivityRecorder = teams.paperclipRecordAcceptedActivity;\n      if (typeof acceptedActivityRecorder !== \"function\") {\n        throw new TeamsAdapterCompatibilityError(\n          \"durable accepted-activity recorder is unavailable\",\n        );\n      }\n      await acceptedActivityRecorder.call(this.adapter, raw);\n    }\n  }\n\n  async sendTeamsFileConsentCard(\n    threadId: string,\n    card: ReturnType<typeof buildTeamsFileConsentCard>,\n  ): Promise<{ id: string }> {\n    return await this.sendTeamsFileCard(threadId, \"consent\", card);\n  }\n\n  async sendTeamsUploadedFileCard(\n    threadId: string,\n    card: ReturnType<typeof buildTeamsUploadedFileCard>,\n  ): Promise<{ id: string }> {","sourceCodeStart":2536,"sourceCodeEnd":2572,"githubUrl":"https://github.com/paperclipai/paperclip/blob/01ad8584922b5d85292b1723cae71fa0d9b07a19/server/src/services/chat-sdk-runtime.ts#L2536-L2572","documentation":"When recordMicrosoftTeamsRoute receives a raw activity, it additionally persists the accepted activity via teams.paperclipRecordAcceptedActivity. If the adapter implements the route recorder but not the accepted-activity recorder, the route is saved first and then the runtime throws TeamsAdapterCompatibilityError, leaving the durable route written without its paired accepted-activity record.","triggerScenarios":"Calling recordMicrosoftTeamsRoute(threadId, serviceUrl, raw) with raw !== undefined on a microsoft-teams adapter that defines paperclipRecordThreadServiceUrl but omits paperclipRecordAcceptedActivity.","commonSituations":"Partially upgraded adapter that gained route recording in one release and accepted-activity recording in a later one; custom Teams adapters copying only part of the internal contract; test doubles implementing the route recorder only.","solutions":["Upgrade the microsoft-teams adapter to a version implementing paperclipRecordAcceptedActivity.","Feature-detect both recorders before calling recordMicrosoftTeamsRoute whenever a raw activity will be passed, or omit the raw argument.","Reconcile durable state after this error: the serviceUrl was already persisted, so re-running with raw undefined avoids duplicate route writes."],"exampleFix":"// before\nawait runtime.recordMicrosoftTeamsRoute(threadId, serviceUrl, raw);\n\n// after\nconst teams = runtime.getProviderAdapter() as {\n  paperclipRecordAcceptedActivity?: unknown;\n};\nawait runtime.recordMicrosoftTeamsRoute(\n  threadId,\n  serviceUrl,\n  typeof teams.paperclipRecordAcceptedActivity === \"function\" ? raw : undefined,\n);","handlingStrategy":"type-guard","validationCode":"const teams = runtime.getProviderAdapter() as Record<string, unknown>;\nif (raw !== undefined && typeof teams.paperclipRecordAcceptedActivity !== \"function\") {\n  raw = undefined; // persist route only, skip accepted-activity recording\n}","typeGuard":"function supportsAcceptedActivityRecording(a: unknown): a is { paperclipRecordAcceptedActivity: (raw: unknown) => Promise<void> } {\n  return typeof a === \"object\" && a !== null && typeof (a as any).paperclipRecordAcceptedActivity === \"function\";\n}","tryCatchPattern":"try {\n  await runtime.recordMicrosoftTeamsRoute(threadId, serviceUrl, raw);\n} catch (err) {\n  if (err instanceof TeamsAdapterCompatibilityError && err.message.includes(\"accepted-activity recorder is unavailable\")) {\n    await runtime.recordMicrosoftTeamsRoute(threadId, serviceUrl); // route already stored; finish without raw\n  } else throw err;\n}","preventionTips":["Upgrade the Teams adapter so both recorders ship together.","Feature-detect both paperclipRecordThreadServiceUrl and paperclipRecordAcceptedActivity before passing raw.","Note the partial-write hazard: the route is stored before this throw; retry without raw to reconcile."],"tags":["microsoft-teams","adapter-compatibility","routing","missing-capability"],"backgroundTag":"method-not-implemented","analyzedSha":"01ad8584922b5d85292b1723cae71fa0d9b07a19","analyzedAt":"2026-09-10T03:14:50.855Z","contentChangedAt":"2026-09-10T03:14:50.855Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}