{"record":{"id":"1196cbec13090a30","repo":"mastra-ai/mastra","slug":"if-the-attachment-is-not-an-image-or-text-it-must","errorCode":null,"errorMessage":"If the attachment is not an image or text, it must specify a content type","messagePattern":"If the attachment is not an image or text, it must specify a content type","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/core/src/agent/message-list/prompt/attachments-to-parts.ts","lineNumber":81,"sourceCode":"        break;\n      }\n\n      case 'data:': {\n        if (attachment.contentType?.startsWith('image/')) {\n          parts.push({\n            type: 'image',\n            image: urlString,\n            mimeType: attachment.contentType,\n          });\n        } else if (attachment.contentType?.startsWith('text/')) {\n          parts.push({\n            type: 'file',\n            data: urlString,\n            mimeType: attachment.contentType,\n          });\n        } else {\n          if (!attachment.contentType) {\n            throw new Error('If the attachment is not an image or text, it must specify a content type');\n          }\n\n          parts.push({\n            type: 'file',\n            data: urlString,\n            mimeType: attachment.contentType,\n          });\n        }\n\n        break;\n      }\n\n      default: {\n        throw new Error(`Unsupported URL protocol: ${url.protocol}`);\n      }\n    }\n  }\n","sourceCodeStart":63,"sourceCodeEnd":99,"githubUrl":"https://github.com/mastra-ai/mastra/blob/75dd419e613fe9c39f846ffc500716141b74fda6/packages/core/src/agent/message-list/prompt/attachments-to-parts.ts#L63-L99","documentation":"For data: / inline (non-remote-URL) attachments, attachmentsToParts can handle images and text without an explicit type, but any other kind must carry a contentType to become a valid 'file' part. If contentType is missing for a non-image, non-text attachment, this Error is thrown.","triggerScenarios":"Adding an inline/data: attachment that is neither image/* nor text/* and lacks contentType — e.g. { url: 'data:application/octet-stream;base64,...' } with contentType omitted, or a binary attachment where MIME detection fails.","commonSituations":"Drag-and-drop uploads of arbitrary files where the client doesn't send a MIME type; binary blobs mislabeled as text; base64-encoded files reconstructed server-side without stored metadata.","solutions":["Provide contentType on the attachment (e.g. 'audio/mpeg', 'application/json').","If the data is actually text, ensure it is recognized as text (or set contentType 'text/plain') so it follows the text path.","Sniff the MIME type server-side (e.g. file-type / magic bytes) before building the attachment.","Reject attachments without a MIME type at your API boundary."],"exampleFix":"// before\n{ url: audioDataUrl } // no contentType, detected as binary\n// after\n{ url: audioDataUrl, contentType: 'audio/mpeg' }","handlingStrategy":"validation","validationCode":"function assertInlineAttachment(a: { url: string; contentType?: string }) {\n  const isImage = a.contentType?.startsWith('image/');\n  const isText = a.contentType?.startsWith('text/');\n  if (!isImage && !isText && !a.contentType) {\n    throw new Error(`Non-image/text attachment ${a.url.slice(0, 40)}… needs contentType`);\n  }\n}","typeGuard":"function isTypedAttachment(a: { url: string; contentType?: string }): a is { url: string; contentType: string } {\n  const img = a.contentType?.startsWith('image/');\n  const txt = a.contentType?.startsWith('text/');\n  return Boolean(img || txt || a.contentType);\n}","tryCatchPattern":"try {\n  parts = attachmentsToParts([attachment]);\n} catch (e) {\n  if (e instanceof Error && e.message.includes('must specify a content type')) {\n    const sniffed = await sniffMimeType(attachment.data);\n    parts = attachmentsToParts([{ ...attachment, contentType: sniffed }]);\n  } else throw e;\n}","preventionTips":["Sniff magic bytes server-side for base64/data: uploads missing MIME metadata.","Reject binary uploads without a detected MIME type at intake.","Label text payloads explicitly as text/* so they take the text path."],"tags":["attachment","mime-type","validation"],"backgroundTag":"missing-content-type","analyzedSha":"75dd419e613fe9c39f846ffc500716141b74fda6","analyzedAt":"2026-08-30T00:15:31.844Z","schemaVersion":2},"datasetVersion":"2026-08-30T03:17:51.788Z"}