paperclipai/paperclip · error · TeamsServiceUrlValidationError
Teams destination is missing its conversation identity
Error message
Teams destination is missing its conversation identity
What it means
withThreadServiceUrl decodes a Paperclip thread id back into Teams coordinates and requires a non-empty conversationId, because all outbound calls must be routed to the conversation's persisted or decoded service URL. If decodeThreadId yields no conversation identity, the destination is unusable and TeamsServiceUrlValidationError is thrown.
Source
Thrown at server/src/services/chat-sdk-runtime.ts:1195
serviceUrl: string,
http: unknown,
settings?: unknown,
) => TeamsApiClientInternals;
const scopedApi = new ApiClient(
serviceUrl,
defaultApi.http,
defaultApi._apiClientSettings,
);
return await apiScope.run(scopedApi, operation);
};
const withThreadServiceUrl = async <T>(
threadId: string,
operation: () => Promise<T>,
requireRoute = false,
): Promise<T> => {
const decoded = teams.decodeThreadId!(threadId);
if (typeof decoded.conversationId !== "string" || !decoded.conversationId) {
throw new TeamsServiceUrlValidationError(
"Teams destination is missing its conversation identity",
);
}
const persistedServiceUrl = await teams.chat
?.getState()
.get(teamsConversationRouteStateKey(decoded.conversationId));
if (
requireRoute &&
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,View on GitHub (pinned to 01ad858492)
Solutions
- Verify the threadId originates from the Teams adapter (teams.decodeThreadId should round-trip it) and is not empty.
- Log the decoded object to inspect what decodeThreadId returns for your id format.
- Migrate any persisted legacy ids so they carry a conversation identity.
- Guard call sites: only invoke Teams outbound APIs with ids produced by the Teams adapter's encodeThreadId.
Example fix
// before
await sendTeamsMessage(someOtherAdapterThreadId, text); // foreign id
// after
if (!threadId?.startsWith('teams:')) return;
await sendTeamsMessage(threadId, text); // Teams-adapter-issued id with conversationId Defensive patterns
Strategy: validation
Validate before calling
const decoded = teams.decodeThreadId(threadId);
if (typeof decoded?.conversationId !== 'string' || !decoded.conversationId) {
throw new Error(`threadId ${threadId} has no Teams conversation identity`);
} Type guard
function isTeamsThreadId(threadId: string): boolean {
try { return Boolean(teams.decodeThreadId(threadId)?.conversationId); } catch { return false; }
} Try / catch
try {
await sendViaTeams(threadId, payload);
} catch (err) {
if (err instanceof TeamsServiceUrlValidationError && err.message.includes('conversation identity')) {
logger.warn(`Skipping non-Teams or malformed threadId: ${threadId}`);
}
throw err;
} Prevention
- Only pass thread ids produced by the Teams adapter's own encoding into Teams outbound calls.
- Namespace persisted thread ids by adapter so foreign ids never reach Teams egress.
- Add round-trip tests: encodeThreadId -> decodeThreadId must preserve conversationId.
When it happens
Trigger: Calling any thread-scoped outbound operation (send, postActivity, file card send, etc.) with a threadId whose decoded form has no conversationId — e.g. a malformed/legacy thread id, a non-Teams id passed into a Teams path, or an empty string id.
Common situations: Storing thread ids from a different chat adapter and passing them to Teams egress; truncating or corrupting persisted thread ids in your own database; legacy thread-id formats that decodeThreadId cannot parse into a conversation id.
Understand the failure class
Background: "missing required argument" and "the following required arguments were not provided": what required-argument errors mean and how to fix them — this error's family across 20 libraries.
Related errors
- CHAT_PROVIDER_PRETRANSPORT_REJECTED
- Teams file destination is missing its verified route
- invalid file-card shape
- Teams file cards require an exact personal conversation
- Image exceeds attachment bound
AI-assisted analysis of paperclipai/paperclip@01ad858492 (2026-09-10).
Data as JSON: /api/errors/0545c1842ad8e899.
Report an issue: GitHub.