{"record":{"id":"3ea950b4b070f72e","repo":"paperclipai/paperclip","slug":"file-consent-app-hooks-are-unavailable","errorCode":null,"errorMessage":"file-consent App hooks are unavailable","messagePattern":"file-consent App hooks are unavailable","errorType":"exception","errorClass":"TeamsAdapterCompatibilityError","httpStatus":null,"severity":"error","filePath":"server/src/services/chat-sdk-runtime.ts","lineNumber":1222,"sourceCode":"      persistedServiceUrl == null &&\n      decoded.serviceUrl == null\n    ) {\n      throw new TeamsServiceUrlValidationError(\n        \"Teams file destination is missing its verified route\",\n      );\n    }\n    return await withServiceUrl(\n      persistedServiceUrl ?? decoded.serviceUrl ?? defaultApi.serviceUrl,\n      operation,\n    );\n  };\n\n  if (enableFileConsent) {\n    if (\n      typeof teams.app.send !== \"function\" ||\n      typeof teams.app.on !== \"function\"\n    ) {\n      throw new TeamsAdapterCompatibilityError(\n        \"file-consent App hooks are unavailable\",\n      );\n    }\n    teams.paperclipSendFileCard = async (threadId, kind, input) => {\n      const card =\n        kind === \"consent\"\n          ? parseTeamsFileConsentCard(input)\n          : kind === \"file_info\"\n            ? parseTeamsUploadedFileCard(input)\n            : null;\n      if (!card)\n        throw new TeamsAdapterCompatibilityError(\"invalid file-card shape\");\n      const decoded = teams.decodeThreadId!(threadId);\n      // Never infer personal scope from a missing type or conversation prefix.\n      if (\n        decoded.conversationType !== \"personal\" ||\n        typeof decoded.conversationId !== \"string\" ||\n        !decoded.conversationId ||","sourceCodeStart":1204,"sourceCodeEnd":1240,"githubUrl":"https://github.com/paperclipai/paperclip/blob/01ad8584922b5d85292b1723cae71fa0d9b07a19/server/src/services/chat-sdk-runtime.ts#L1204-L1240","documentation":"File-consent support (enableFileConsent = true) requires the Teams App object to expose send and on hooks: the wrapper registers handlers via app.on and delivers provider-native file cards via app.send inside the routed ALS scope. If either hook is missing, the adapter surface is incompatible with the file-consent feature and this compatibility error is thrown at wiring time.","triggerScenarios":"Calling scopeMicrosoftTeamsEgress(adapter, configuredApiUrl, true) where typeof teams.app.send !== 'function' or typeof teams.app.on !== 'function' — e.g. a minimal/mocked app object, or an adapter build that omits the App event hooks.","commonSituations":"Enabling file consent in tests with a partial app mock; an older adapter package whose App object lacked send/on under those names; accidentally passing app.api (the client) where the app object is expected.","solutions":["Use the full Teams adapter App object that implements send and on.","Enable file consent only with an adapter version known to support App hooks; otherwise pass enableFileConsent = false.","Extend test doubles with send: async () => {} and on: () => {} stubs.","Confirm you are passing the app object, not app.api, into the wrapper's expectations."],"exampleFix":"// before\nscopeMicrosoftTeamsEgress(adapter, apiUrl, true); // app lacks send/on\n// after\n// either stub the hooks in tests\napp.send = async () => ({});\napp.on = () => {};\nscopeMicrosoftTeamsEgress(adapter, apiUrl, true);\n// or disable the feature\nscopeMicrosoftTeamsEgress(adapter, apiUrl, false);","handlingStrategy":"validation","validationCode":"if (enableFileConsent && (typeof adapter.app?.send !== 'function' || typeof adapter.app?.on !== 'function')) {\n  throw new Error('File consent requires a Teams App with send and on hooks');\n}","typeGuard":"function supportsFileConsentHooks(app: unknown): app is { send: Function; on: Function } {\n  return typeof app === 'object' && app !== null && typeof (app as any).send === 'function' && typeof (app as any).on === 'function';\n}","tryCatchPattern":"try {\n  scoped = scopeMicrosoftTeamsEgress(adapter, apiUrl, enableFileConsent);\n} catch (err) {\n  if (err instanceof TeamsAdapterCompatibilityError && err.message.includes('file-consent App hooks')) {\n    logger.error('File consent enabled but the adapter App lacks send/on — upgrade adapter or disable file consent');\n  }\n  throw err;\n}","preventionTips":["Gate enableFileConsent on an adapter capability check rather than config alone.","Keep test app doubles equipped with send/on stubs.","Verify adapter version supports App-level hooks before shipping file-consent features."],"tags":["teams","file-consent","adapter-compatibility"],"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-14T00:17:10.932Z"}