{"record":{"id":"f0883398115a8186","repo":"paperclipai/paperclip","slug":"durable-accepted-activity-state-is-unavailable","errorCode":null,"errorMessage":"durable accepted-activity state is unavailable","messagePattern":"durable accepted-activity state is unavailable","errorType":"exception","errorClass":"TeamsAdapterCompatibilityError","httpStatus":null,"severity":"error","filePath":"server/src/services/chat-sdk-runtime.ts","lineNumber":1076,"sourceCode":"  teams.cacheUserContext = () => {};\n  teams.getIncomingUser = async () => null;\n  teams.getUser = async () => null;\n\n  teams.paperclipRecordAcceptedActivity = async (activityValue: unknown) => {\n    if (!isRecord(activityValue)) return;\n    const from = isRecord(activityValue.from) ? activityValue.from : null;\n    const conversation = isRecord(activityValue.conversation)\n      ? activityValue.conversation\n      : null;\n    const channelData = isRecord(activityValue.channelData)\n      ? activityValue.channelData\n      : null;\n    const userId =\n      typeof from?.id === \"string\" && from.id.length > 0 ? from.id : null;\n    if (!userId) return;\n    const state = teams.chat?.getState();\n    if (!state) {\n      throw new TeamsAdapterCompatibilityError(\n        \"durable accepted-activity state is unavailable\",\n      );\n    }\n    const ttl = TEAMS_ACCEPTED_ACTIVITY_CACHE_TTL_MS;\n    const writes: Promise<void>[] = [];\n    if (activityValue.serviceUrl !== undefined) {\n      writes.push(\n        state.set(\n          `teams:serviceUrl:${userId}`,\n          trustedTeamsServiceUrl(\n            activityValue.serviceUrl,\n            trustedConfiguredApiUrl,\n          ),\n          ttl,\n        ),\n      );\n    }\n    if (typeof from?.aadObjectId === \"string\" && from.aadObjectId.length > 0) {","sourceCodeStart":1058,"sourceCodeEnd":1094,"githubUrl":"https://github.com/paperclipai/paperclip/blob/01ad8584922b5d85292b1723cae71fa0d9b07a19/server/src/services/chat-sdk-runtime.ts#L1058-L1094","documentation":"paperclipRecordAcceptedActivity persists verified routing state (serviceUrl, aadObjectId, tenantId, channel context) via the adapter's durable chat state store obtained from teams.chat?.getState(). If that returns null/undefined there is no durable store to write to, and recording an accepted activity would silently lose the routing data Microsoft requires — so the wrapper throws this compatibility error instead.","triggerScenarios":"An accepted Teams activity arrives with a from.id, and teams.chat is undefined or teams.chat.getState() returns a falsy value — e.g. an adapter without chat state middleware configured, or state not yet initialized.","commonSituations":"Running the Teams adapter without its persistence/state plugin enabled; adapter package versions where chat state access moved to a different accessor; test adapters lacking a chat/state implementation while simulating inbound activities.","solutions":["Configure the Teams adapter's chat state (teams.chat with a working getState()) before wiring scopeMicrosoftTeamsEgress.","Upgrade/pin the Teams adapter so the chat state accessor matches TeamsAdapterInternals.","Provide a stateful test double for teams.chat.getState() in unit tests.","Check adapter initialization order — state middleware must be attached before the first activity is processed."],"exampleFix":"// before\nconst adapter = createTeamsAdapter(); // chat/state middleware not attached\nscopeMicrosoftTeamsEgress(adapter);\n// after\nconst adapter = createTeamsAdapter();\nadapter.useTeamsChatState(store); // ensures teams.chat.getState() returns a state object\nscopeMicrosoftTeamsEgress(adapter);","handlingStrategy":"try-catch","validationCode":"const state = adapter.chat?.getState?.();\nif (!state) throw new Error('Teams chat state store must be initialized before accepting activities');","typeGuard":"function hasDurableState(a: unknown): a is { chat: { getState: () => NonNullable<unknown> } } {\n  const chat = (a as any)?.chat;\n  return !!chat && typeof chat.getState === 'function' && !!chat.getState();\n}","tryCatchPattern":"try {\n  await teams.paperclipRecordAcceptedActivity(activity);\n} catch (err) {\n  if (err instanceof TeamsAdapterCompatibilityError && err.message.includes('durable accepted-activity state')) {\n    logger.error('Teams chat state missing — attach state middleware before processing activities');\n  }\n  throw err;\n}","preventionTips":["Attach the adapter's chat/state middleware during bootstrap, before any inbound activity can be accepted.","In tests, supply a teams.chat double whose getState returns a real in-memory state object.","Monitor adapter upgrades for changes to the chat/state accessor shape."],"tags":["teams","adapter-compatibility","state"],"backgroundTag":"internal-invariant-violation","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"}