paperclipai/paperclip · error · TeamsAdapterCompatibilityError

app.api cannot be scoped per asynchronous conversation

Error message

app.api cannot be scoped per asynchronous conversation

What it means

To scope egress, the wrapper redefines teams.app.api with a getter backed by AsyncLocalStorage so each conversation sees its own API client. If the app object's 'api' property is a non-configurable own property, Object.defineProperty would throw a TypeError, so the wrapper pre-empts it with this compatibility error: the adapter's app.api cannot be re-scoped per asynchronous conversation.

Source

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

  if (typeof teams.getUser !== "function") {
    throw new TeamsAdapterCompatibilityError("getUser is unavailable");
  }
  for (const methodName of TEAMS_THREAD_SCOPED_METHODS) {
    if (typeof teams[methodName] !== "function") {
      throw new TeamsAdapterCompatibilityError(`${methodName} is unavailable`);
    }
  }
  if (
    typeof teams.app.api.constructor !== "function" ||
    !("http" in teams.app.api)
  ) {
    throw new TeamsAdapterCompatibilityError(
      "the API client constructor or HTTP transport is unavailable",
    );
  }
  const apiDescriptor = Object.getOwnPropertyDescriptor(teams.app, "api");
  if (apiDescriptor && apiDescriptor.configurable === false) {
    throw new TeamsAdapterCompatibilityError(
      "app.api cannot be scoped per asynchronous conversation",
    );
  }
  const trustedConfiguredApiUrl = configuredApiUrl
    ? normalizedTeamsServiceUrl(configuredApiUrl)
    : null;

  // The pinned Teams adapter caches activity/user metadata and may query the
  // members or Graph APIs before Chat dispatches to Paperclip's reach and
  // tenant checks. Keep authenticated but unadmitted events observationally
  // inert. Accepted activities explicitly persist the minimum routing context
  // through paperclipRecordAcceptedActivity below.
  teams.cacheUserContext = () => {};
  teams.getIncomingUser = async () => null;
  teams.getUser = async () => null;

  teams.paperclipRecordAcceptedActivity = async (activityValue: unknown) => {
    if (!isRecord(activityValue)) return;

View on GitHub (pinned to 01ad858492)

Solutions

  1. Remove any Object.freeze()/Object.seal() on the Teams app object before passing it to scopeMicrosoftTeamsEgress.
  2. If app.api was defined with defineProperty, set configurable: true.
  3. Expose api as a normal property (this.api = client) rather than a locked own property.
  4. Check adapter version notes: if the SDK made api non-configurable, upgrade Paperclip's wrapper or pin the older SDK.

Example fix

// before
const app = { id: 'x' };
Object.defineProperty(app, 'api', { value: client, configurable: false });
// after
Object.defineProperty(app, 'api', { value: client, writable: true, configurable: true, enumerable: true });
Defensive patterns

Strategy: validation

Validate before calling

const d = Object.getOwnPropertyDescriptor(adapter.app, 'api');
if (d && d.configurable === false) {
  throw new Error('app.api must be configurable to be scoped per conversation');
}

Try / catch

try {
  scoped = scopeMicrosoftTeamsEgress(adapter);
} catch (err) {
  if (err instanceof TeamsAdapterCompatibilityError && err.message.includes('cannot be scoped')) {
    logger.error('app.api is non-configurable (frozen/sealed app?) — unfreeze before scoping');
  }
  throw err;
}

Prevention

When it happens

Trigger: The adapter (or test harness) defined app.api with Object.defineProperty(..., { configurable: false }) or froze/sealed the app object (Object.freeze(app)), making 'api' non-configurable.

Common situations: App objects frozen for immutability in tests; custom adapter code that locks down the api property; adapter versions that changed how app.api is exposed from a configurable accessor to a fixed value.

Understand the failure class

Background: "is not a compatible type" / "cannot merge" errors: when a value's type doesn't match what the library requires — this error's family across 65 libraries.

Related errors


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