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
- Remove any Object.freeze()/Object.seal() on the Teams app object before passing it to scopeMicrosoftTeamsEgress.
- If app.api was defined with defineProperty, set configurable: true.
- Expose api as a normal property (this.api = client) rather than a locked own property.
- 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
- Do not Object.freeze/Object.seal the Teams app object passed into scopeMicrosoftTeamsEgress.
- Define api with configurable: true if you must use defineProperty.
- Document that the wrapper owns the app.api property for the process lifetime.
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
- decodeThreadId is unavailable
- openDM is unavailable
- cacheUserContext is unavailable
- getIncomingUser is unavailable
- getUser is unavailable
AI-assisted analysis of paperclipai/paperclip@01ad858492 (2026-09-10).
Data as JSON: /api/errors/61ee76603a23f4d6.
Report an issue: GitHub.