paperclipai/paperclip · error · TeamsAdapterCompatibilityError
openDM is unavailable
Error message
openDM is unavailable
What it means
Same compatibility gate as error 537: after verifying decodeThreadId, the runtime checks that adapter.openDM is a function and throws TeamsAdapterCompatibilityError('openDM is unavailable') when it is not. The adapter therefore cannot open direct-message threads and is rejected.
Source
Thrown at server/src/services/chat-sdk-runtime.ts:1019
* outbound call to a fresh API client rooted at that route. Legacy thread ids
* that embedded a URL remain readable, but a newer persisted route wins. A
* context-local getter keeps simultaneous conversations isolated without
* forcing unrelated Teams threads through a single network queue.
*/
export function scopeMicrosoftTeamsEgress(
adapter: Adapter,
configuredApiUrl?: string,
enableFileConsent = false,
): Adapter {
const teams = adapter as unknown as TeamsAdapterInternals;
if (!teams.app?.api) {
throw new TeamsAdapterCompatibilityError("app.api is unavailable");
}
if (typeof teams.decodeThreadId !== "function") {
throw new TeamsAdapterCompatibilityError("decodeThreadId is unavailable");
}
if (typeof teams.openDM !== "function") {
throw new TeamsAdapterCompatibilityError("openDM is unavailable");
}
if (typeof teams.cacheUserContext !== "function") {
throw new TeamsAdapterCompatibilityError("cacheUserContext is unavailable");
}
if (typeof teams.getIncomingUser !== "function") {
throw new TeamsAdapterCompatibilityError("getIncomingUser is unavailable");
}
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)View on GitHub (pinned to 01ad858492)
Solutions
- Update the Teams adapter package to the version matching the runtime's expected internals.
- Add/implement openDM on a custom adapter per the runtime contract.
- Extend the mock with openDM: vi.fn().
- Confirm no wrapper/proxy around the adapter deletes or renames openDM.
Example fix
// before
const adapter = { app: { api: {} }, decodeThreadId: vi.fn() };
// after
const adapter = { app: { api: {} }, decodeThreadId: vi.fn(), openDM: vi.fn() }; Defensive patterns
Strategy: type-guard
Validate before calling
const teams = adapter as unknown as TeamsAdapterInternals;
if (typeof teams.openDM !== "function") throw new Error("adapter missing openDM"); Type guard
function supportsOpenDM(a: unknown): a is TeamsAdapterInternals {
return typeof (a as TeamsAdapterInternals)?.openDM === "function";
} Try / catch
try {
wireTeamsAdapter(adapter);
} catch (e) {
if (e instanceof TeamsAdapterCompatibilityError && e.message.includes("openDM")) {
// implement or upgrade so openDM exists
}
} Prevention
- Feature-check the full required method set, not just one method, before wiring.
- Upgrade the adapter package when the runtime adds new internal requirements.
- Extend mocks whenever a new compatibility error names a missing method.
- Add a unit test asserting every internal method is a function on the adapter.
When it happens
Trigger: Wiring a Teams adapter whose internals include app.api and decodeThreadId but whose openDM property is missing or not a function (older adapter version, incomplete custom adapter, partial mock).
Common situations: Adapter package drift after a dependency update; a custom adapter implemented before openDM was added to the contract; mocks that stub only the methods a specific test touched.
Related errors
- decodeThreadId is unavailable
- cacheUserContext is unavailable
- getIncomingUser is unavailable
- getUser is unavailable
- ${methodName} is unavailable
AI-assisted analysis of paperclipai/paperclip@01ad858492 (2026-09-10).
Data as JSON: /api/errors/d4f6ac371f59612d.
Report an issue: GitHub.