{"record":{"id":"4e5e2aa3444f2956","repo":"paperclipai/paperclip","slug":"invalid-file-card-shape","errorCode":null,"errorMessage":"invalid file-card shape","messagePattern":"invalid file-card shape","errorType":"validation","errorClass":"TeamsAdapterCompatibilityError","httpStatus":null,"severity":"error","filePath":"server/src/services/chat-sdk-runtime.ts","lineNumber":1234,"sourceCode":"\n  if (enableFileConsent) {\n    if (\n      typeof teams.app.send !== \"function\" ||\n      typeof teams.app.on !== \"function\"\n    ) {\n      throw new TeamsAdapterCompatibilityError(\n        \"file-consent App hooks are unavailable\",\n      );\n    }\n    teams.paperclipSendFileCard = async (threadId, kind, input) => {\n      const card =\n        kind === \"consent\"\n          ? parseTeamsFileConsentCard(input)\n          : kind === \"file_info\"\n            ? parseTeamsUploadedFileCard(input)\n            : null;\n      if (!card)\n        throw new TeamsAdapterCompatibilityError(\"invalid file-card shape\");\n      const decoded = teams.decodeThreadId!(threadId);\n      // Never infer personal scope from a missing type or conversation prefix.\n      if (\n        decoded.conversationType !== \"personal\" ||\n        typeof decoded.conversationId !== \"string\" ||\n        !decoded.conversationId ||\n        decoded.conversationId.length > 1024 ||\n        /[\\x00-\\x20\\x7f]/.test(decoded.conversationId) ||\n        /;messageid=/i.test(decoded.conversationId)\n      ) {\n        throw new TeamsServiceUrlValidationError(\n          \"Teams file cards require an exact personal conversation\",\n        );\n      }\n      return await withThreadServiceUrl(\n        threadId,\n        async () => {\n          let result: unknown;","sourceCodeStart":1216,"sourceCodeEnd":1252,"githubUrl":"https://github.com/paperclipai/paperclip/blob/01ad8584922b5d85292b1723cae71fa0d9b07a19/server/src/services/chat-sdk-runtime.ts#L1216-L1252","documentation":"paperclipSendFileCard validates its input with parseTeamsFileConsentCard (for kind 'consent') or parseTeamsUploadedFileCard (for kind 'file_info'). If the kind is neither, or the input fails the parser's shape requirements, the card cannot be turned into a valid Teams attachment, so this compatibility error is thrown before any network call.","triggerScenarios":"Calling teams.paperclipSendFileCard(threadId, kind, input) with kind not in {'consent','file_info'}, or input missing fields the parsers require (e.g. no name/acceptContext for consent, no fileId/name for file_info), or input being null/undefined.","commonSituations":"A typo'd kind string ('consentCard' instead of 'consent'); a caller passing the raw upload result instead of the parsed card payload; schema drift after changing the upstream file metadata producer so required fields disappear.","solutions":["Validate the kind argument is exactly 'consent' or 'file_info' before calling.","Run the input through the same parser (parseTeamsFileConsentCard/parseTeamsUploadedFileCard) at the call site to see which required field is missing.","Fix the upstream producer so the card payload includes all required fields (name, uploadUrl/acceptContext for consent; fileId/name/contentUrl for file_info).","Add a unit test feeding representative payloads into paperclipSendFileCard to catch shape drift."],"exampleFix":"// before\nawait teams.paperclipSendFileCard(threadId, 'consent', { foo: 1 }); // invalid shape\n// after\nawait teams.paperclipSendFileCard(threadId, 'consent', {\n  name: 'report.pdf',\n  uploadUrl: uploadCtx.uploadUrl,\n  contentUrl: uploadCtx.contentUrl,\n  acceptContext: { threadId }\n});","handlingStrategy":"validation","validationCode":"const card = kind === 'consent' ? parseTeamsFileConsentCard(input) : kind === 'file_info' ? parseTeamsUploadedFileCard(input) : null;\nif (!card) throw new Error(`Invalid ${kind} file-card payload`);","typeGuard":"function isFileCardKind(k: string): k is 'consent' | 'file_info' {\n  return k === 'consent' || k === 'file_info';\n}","tryCatchPattern":"try {\n  await teams.paperclipSendFileCard(threadId, kind, input);\n} catch (err) {\n  if (err instanceof TeamsAdapterCompatibilityError && err.message === 'invalid file-card shape') {\n    logger.error(`Rejected ${kind} file card payload: ${JSON.stringify(input).slice(0, 200)}`);\n  }\n  throw err;\n}","preventionTips":["Validate kind and payload shape at the producer boundary before invoking paperclipSendFileCard.","Share the same parser functions (parseTeamsFileConsentCard/parseTeamsUploadedFileCard) between producer and sender so schemas cannot drift.","Add snapshot/unit tests covering both card kinds and a rejection case for unknown kinds."],"tags":["teams","file-consent","validation"],"backgroundTag":"schema-validation-failed","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"}