{"record":{"id":"4a2221b4f11ea4a6","repo":"paperclipai/paperclip","slug":"decodethreadid-is-unavailable","errorCode":null,"errorMessage":"decodeThreadId is unavailable","messagePattern":"decodeThreadId is unavailable","errorType":"exception","errorClass":"TeamsAdapterCompatibilityError","httpStatus":null,"severity":"error","filePath":"server/src/services/chat-sdk-runtime.ts","lineNumber":1016,"sourceCode":" * requires replies to target that matching URL. The URL is mutable routing\n * state, not conversation identity, so current thread ids omit it. Persist the\n * latest verified route under the stable conversation id and scope each\n * outbound call to a fresh API client rooted at that route. Legacy thread ids\n * that embedded a URL remain readable, but a newer persisted route wins. A\n * context-local getter keeps simultaneous conversations isolated without\n * forcing unrelated Teams threads through a single network queue.\n */\nexport function scopeMicrosoftTeamsEgress(\n  adapter: Adapter,\n  configuredApiUrl?: string,\n  enableFileConsent = false,\n): Adapter {\n  const teams = adapter as unknown as TeamsAdapterInternals;\n  if (!teams.app?.api) {\n    throw new TeamsAdapterCompatibilityError(\"app.api is unavailable\");\n  }\n  if (typeof teams.decodeThreadId !== \"function\") {\n    throw new TeamsAdapterCompatibilityError(\"decodeThreadId is unavailable\");\n  }\n  if (typeof teams.openDM !== \"function\") {\n    throw new TeamsAdapterCompatibilityError(\"openDM is unavailable\");\n  }\n  if (typeof teams.cacheUserContext !== \"function\") {\n    throw new TeamsAdapterCompatibilityError(\"cacheUserContext is unavailable\");\n  }\n  if (typeof teams.getIncomingUser !== \"function\") {\n    throw new TeamsAdapterCompatibilityError(\"getIncomingUser is unavailable\");\n  }\n  if (typeof teams.getUser !== \"function\") {\n    throw new TeamsAdapterCompatibilityError(\"getUser is unavailable\");\n  }\n  for (const methodName of TEAMS_THREAD_SCOPED_METHODS) {\n    if (typeof teams[methodName] !== \"function\") {\n      throw new TeamsAdapterCompatibilityError(`${methodName} is unavailable`);\n    }\n  }","sourceCodeStart":998,"sourceCodeEnd":1034,"githubUrl":"https://github.com/paperclipai/paperclip/blob/01ad8584922b5d85292b1723cae71fa0d9b07a19/server/src/services/chat-sdk-runtime.ts#L998-L1034","documentation":"The Teams adapter compatibility shim inspects internal properties of the installed Teams adapter before wiring it into the chat SDK runtime. If adapter.decodeThreadId is not a function it throws TeamsAdapterCompatibilityError('decodeThreadId is unavailable'), meaning the adapter object does not expose the expected internal API surface.","triggerScenarios":"Passing an adapter (or a mocked/stubbed Teams adapter) whose app internals are set but which lacks a decodeThreadId method — e.g. an adapter package version predating that method, or a partial custom implementation.","commonSituations":"Upgrading or downgrading the Teams adapter package so internals drifted from what the runtime expects; a hand-rolled adapter missing decodeThreadId; a test double that stubs only some methods.","solutions":["Upgrade the Teams adapter package to the version the runtime expects so all internal methods (decodeThreadId, openDM, cacheUserContext, getIncomingUser, getUser) exist.","Implement decodeThreadId on your custom adapter with the same signature the runtime calls.","Fix the test double/stub to include a decodeThreadId function (e.g. vi.fn()).","Check that the object passed as `adapter` is actually the Teams adapter instance, not a wrapper that hides internals."],"exampleFix":"// before: stub missing method\nconst adapter = { app: { api: {} }, openDM: vi.fn() };\n// after\nconst adapter = { app: { api: {} }, decodeThreadId: vi.fn(), openDM: vi.fn(), cacheUserContext: vi.fn(), getIncomingUser: vi.fn(), getUser: vi.fn() };","handlingStrategy":"type-guard","validationCode":"const teams = adapter as unknown as TeamsAdapterInternals;\nif (typeof teams.decodeThreadId !== \"function\") throw new Error(\"adapter missing decodeThreadId\");","typeGuard":"function supportsDecodeThreadId(a: unknown): a is TeamsAdapterInternals {\n  return typeof (a as TeamsAdapterInternals)?.decodeThreadId === \"function\";\n}","tryCatchPattern":"try {\n  wireTeamsAdapter(adapter);\n} catch (e) {\n  if (e instanceof TeamsAdapterCompatibilityError) {\n    // upgrade the adapter package to the version matching the runtime\n  }\n}","preventionTips":["Pin the Teams adapter package version to one known compatible with the runtime.","Before wiring, feature-check all required internals (decodeThreadId, openDM, cacheUserContext, getIncomingUser, getUser).","Keep test doubles complete — stub every method the compatibility gate inspects.","Read adapter release notes when upgrading so internal contract changes are caught early."],"tags":["teams","adapter-compatibility","api-contract"],"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"}