{"record":{"id":"823f8651bea2d236","repo":"paperclipai/paperclip","slug":"getincominguser-is-unavailable","errorCode":null,"errorMessage":"getIncomingUser is unavailable","messagePattern":"getIncomingUser is unavailable","errorType":"exception","errorClass":"TeamsAdapterCompatibilityError","httpStatus":null,"severity":"error","filePath":"server/src/services/chat-sdk-runtime.ts","lineNumber":1025,"sourceCode":"  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  }\n  if (\n    typeof teams.app.api.constructor !== \"function\" ||\n    !(\"http\" in teams.app.api)\n  ) {\n    throw new TeamsAdapterCompatibilityError(\n      \"the API client constructor or HTTP transport is unavailable\",\n    );\n  }\n  const apiDescriptor = Object.getOwnPropertyDescriptor(teams.app, \"api\");","sourceCodeStart":1007,"sourceCodeEnd":1043,"githubUrl":"https://github.com/paperclipai/paperclip/blob/01ad8584922b5d85292b1723cae71fa0d9b07a19/server/src/services/chat-sdk-runtime.ts#L1007-L1043","documentation":"scopeMicrosoftTeamsEgress performs a structural compatibility audit of the Microsoft Teams adapter before wrapping it. getIncomingUser is one of the required adapter methods Paperclip stubs out to keep unadmitted events observationally inert; if the adapter object lacks it, the runtime refuses to proceed rather than silently misrouting Teams traffic. This guards against running against an adapter version or shape that no longer matches the pinned contract.","triggerScenarios":"Calling scopeMicrosoftTeamsEgress with an adapter whose TeamsAdapterInternals does not expose getIncomingUser as a function — e.g. a custom adapter, a mocked adapter missing the method, or a Teams adapter package version that renamed/removed it.","commonSituations":"Upgrading or swapping the Teams adapter package so internal methods changed; passing the wrong adapter (non-Teams) into scopeMicrosoftTeamsEgress; a hand-rolled test double that only implements part of the adapter surface.","solutions":["Check that the adapter passed to scopeMicrosoftTeamsEgress is the real Microsoft Teams adapter and that it defines getIncomingUser (typeof adapter.getIncomingUser === 'function').","Upgrade or pin the Teams adapter package to the version Paperclip expects so the internal method exists.","If using a test double, add getIncomingUser (it may be async () => null since the wrapper replaces it anyway).","Verify the adapter object is not being constructed from a partial type cast that drops methods."],"exampleFix":"// before\nscopeMicrosoftTeamsEgress({ app, decodeThreadId, openDM } as unknown as Adapter);\n// after\nconst teamsAdapter = createTeamsAdapter(); // full adapter including getIncomingUser\nscopeMicrosoftTeamsEgress(teamsAdapter);","handlingStrategy":"validation","validationCode":"if (typeof adapter.getIncomingUser !== 'function') {\n  throw new Error('Teams adapter missing getIncomingUser; incompatible adapter version');\n}","typeGuard":"function hasGetIncomingUser(a: unknown): a is { getIncomingUser: (...args: unknown[]) => unknown } {\n  return typeof a === 'object' && a !== null && typeof (a as any).getIncomingUser === 'function';\n}","tryCatchPattern":"try {\n  scoped = scopeMicrosoftTeamsEgress(adapter);\n} catch (err) {\n  if (err instanceof TeamsAdapterCompatibilityError && /getIncomingUser/.test(err.message)) {\n    logger.error('Teams adapter is missing getIncomingUser — upgrade or replace the adapter package');\n  }\n  throw err;\n}","preventionTips":["Pin the Teams adapter package version Paperclip expects and review breaking changes on upgrade.","Before calling scopeMicrosoftTeamsEgress, assert the full required method surface (app.api, decodeThreadId, openDM, cacheUserContext, getIncomingUser, getUser).","Never spread-copy adapter instances; keep the prototype chain intact.","In CI, add a smoke test that wraps the real adapter with scopeMicrosoftTeamsEgress."],"tags":["teams","adapter-compatibility","runtime-check"],"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"}