paperclipai/paperclip · error · TeamsAdapterCompatibilityError

file-consent App hooks are unavailable

Error message

file-consent App hooks are unavailable

What it means

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.

Source

Thrown at server/src/services/chat-sdk-runtime.ts:1222

      persistedServiceUrl == null &&
      decoded.serviceUrl == null
    ) {
      throw new TeamsServiceUrlValidationError(
        "Teams file destination is missing its verified route",
      );
    }
    return await withServiceUrl(
      persistedServiceUrl ?? decoded.serviceUrl ?? defaultApi.serviceUrl,
      operation,
    );
  };

  if (enableFileConsent) {
    if (
      typeof teams.app.send !== "function" ||
      typeof teams.app.on !== "function"
    ) {
      throw new TeamsAdapterCompatibilityError(
        "file-consent App hooks are unavailable",
      );
    }
    teams.paperclipSendFileCard = async (threadId, kind, input) => {
      const card =
        kind === "consent"
          ? parseTeamsFileConsentCard(input)
          : kind === "file_info"
            ? parseTeamsUploadedFileCard(input)
            : null;
      if (!card)
        throw new TeamsAdapterCompatibilityError("invalid file-card shape");
      const decoded = teams.decodeThreadId!(threadId);
      // Never infer personal scope from a missing type or conversation prefix.
      if (
        decoded.conversationType !== "personal" ||
        typeof decoded.conversationId !== "string" ||
        !decoded.conversationId ||

View on GitHub (pinned to 01ad858492)

Solutions

  1. Use the full Teams adapter App object that implements send and on.
  2. Enable file consent only with an adapter version known to support App hooks; otherwise pass enableFileConsent = false.
  3. Extend test doubles with send: async () => {} and on: () => {} stubs.
  4. Confirm you are passing the app object, not app.api, into the wrapper's expectations.

Example fix

// before
scopeMicrosoftTeamsEgress(adapter, apiUrl, true); // app lacks send/on
// after
// either stub the hooks in tests
app.send = async () => ({});
app.on = () => {};
scopeMicrosoftTeamsEgress(adapter, apiUrl, true);
// or disable the feature
scopeMicrosoftTeamsEgress(adapter, apiUrl, false);
Defensive patterns

Strategy: validation

Validate before calling

if (enableFileConsent && (typeof adapter.app?.send !== 'function' || typeof adapter.app?.on !== 'function')) {
  throw new Error('File consent requires a Teams App with send and on hooks');
}

Type guard

function supportsFileConsentHooks(app: unknown): app is { send: Function; on: Function } {
  return typeof app === 'object' && app !== null && typeof (app as any).send === 'function' && typeof (app as any).on === 'function';
}

Try / catch

try {
  scoped = scopeMicrosoftTeamsEgress(adapter, apiUrl, enableFileConsent);
} catch (err) {
  if (err instanceof TeamsAdapterCompatibilityError && err.message.includes('file-consent App hooks')) {
    logger.error('File consent enabled but the adapter App lacks send/on — upgrade adapter or disable file consent');
  }
  throw err;
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of paperclipai/paperclip@01ad858492 (2026-09-10). Data as JSON: /api/errors/3ea950b4b070f72e. Report an issue: GitHub.