{"record":{"id":"338945cb30bc3fb4","repo":"paperclipai/paperclip","slug":"teams-destination-is-missing-its-verified-service","errorCode":null,"errorMessage":"Teams destination is missing its verified service URL","messagePattern":"Teams destination is missing its verified service URL","errorType":"validation","errorClass":"TeamsServiceUrlValidationError","httpStatus":null,"severity":"error","filePath":"server/src/services/chat-sdk-runtime.ts","lineNumber":947,"sourceCode":"  ) => Promise<void>;\n  paperclipSendFileCard?: (\n    threadId: string,\n    kind: \"consent\" | \"file_info\",\n    card: unknown,\n  ) => Promise<{ id: string }>;\n  [key: string]: unknown;\n}\n\nfunction teamsConversationRouteStateKey(conversationId: string): string {\n  const baseConversationId = conversationId.replace(/;messageid=[^;]+/i, \"\");\n  return `teams:serviceUrl:conversation:${Buffer.from(baseConversationId).toString(\"base64url\")}`;\n}\n\nconst TEAMS_ACCEPTED_ACTIVITY_CACHE_TTL_MS = 30 * 24 * 60 * 60 * 1_000;\n\nfunction normalizedTeamsServiceUrl(value: unknown): string {\n  if (typeof value !== \"string\" || value.length > 2048) {\n    throw new TeamsServiceUrlValidationError(\n      \"Teams destination is missing its verified service URL\",\n    );\n  }\n  let parsed: URL;\n  try {\n    parsed = new URL(value);\n  } catch {\n    throw new TeamsServiceUrlValidationError(\n      \"Teams destination contains an invalid service URL\",\n    );\n  }\n  if (\n    parsed.protocol !== \"https:\" ||\n    parsed.username ||\n    parsed.password ||\n    parsed.search ||\n    parsed.hash\n  ) {","sourceCodeStart":929,"sourceCodeEnd":965,"githubUrl":"https://github.com/paperclipai/paperclip/blob/01ad8584922b5d85292b1723cae71fa0d9b07a19/server/src/services/chat-sdk-runtime.ts#L929-L965","documentation":"normalizedTeamsServiceUrl validates the serviceUrl of a Microsoft Teams destination before it is used for Bot Connector calls. It throws TeamsServiceUrlValidationError with this message when the value is not a string or exceeds 2048 characters, i.e. the destination has no usable verified service URL at all.","triggerScenarios":"Passing undefined/null/non-string (or a string longer than 2048 chars) as the Teams serviceUrl into the normalizedTeamsServiceUrl path of chat-sdk-runtime.ts, typically when constructing a Teams destination from stored conversation data.","commonSituations":"A Teams activity was saved without serviceUrl; a DB row was corrupted or truncated; code passed a config field name typo so undefined reached the validator; a maliciously crafted payload omitted serviceUrl.","solutions":["Ensure the incoming Bot Framework activity's serviceUrl is read and stored before constructing the destination.","Validate presence and typeof serviceUrl === 'string' before calling into the Teams runtime.","Fix the source of the missing/corrupt destination record (re-save from a fresh Teams activity).","Check for field-name typos or schema drift between the stored destination and the expected serviceUrl field."],"exampleFix":"// before\nconst dest = makeTeamsDestination({ channelData: activity.channelData });\n// after\nif (typeof activity.serviceUrl !== \"string\") throw new Error(\"activity.serviceUrl missing\");\nconst dest = makeTeamsDestination({ serviceUrl: activity.serviceUrl });","handlingStrategy":"type-guard","validationCode":"if (typeof dest.serviceUrl !== \"string\" || dest.serviceUrl.length > 2048) {\n  throw new Error(\"destination.serviceUrl missing or too long\");\n}","typeGuard":"function hasServiceUrl(dest: unknown): dest is { serviceUrl: string } {\n  return typeof dest === \"object\" && dest !== null &&\n    \"serviceUrl\" in dest && typeof (dest as { serviceUrl: unknown }).serviceUrl === \"string\";\n}","tryCatchPattern":"try {\n  const url = normalizedTeamsServiceUrl(dest.serviceUrl);\n} catch (e) {\n  if (e instanceof TeamsServiceUrlValidationError) {\n    // re-fetch destination from a fresh Teams activity; do not attempt connector calls\n  }\n}","preventionTips":["Always copy activity.serviceUrl from the incoming Bot Framework activity into the stored destination.","Reject/flag destination records missing serviceUrl at write time.","Check for schema or field-name drift when mapping stored rows to destination objects.","Cap and validate URL length before persisting."],"tags":["teams","url-validation","bot-framework"],"backgroundTag":"missing-required-argument","analyzedSha":"01ad8584922b5d85292b1723cae71fa0d9b07a19","analyzedAt":"2026-09-10T03:14:50.855Z","contentChangedAt":"2026-09-10T03:14:50.855Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}