{"record":{"id":"26031d3bea8701d8","repo":"vitest-dev/vitest","slug":"test-attachment-requires-only-one-of-body-or-pa","errorCode":null,"errorMessage":"Test attachment requires only one of \"body\" or \"path\" to be set. Both are specified.","messagePattern":"Test attachment requires only one of \"body\" or \"path\" to be set\\. Both are specified\\.","errorType":"validation","errorClass":"TypeError","httpStatus":null,"severity":"error","filePath":"packages/vitest/src/runtime/runner/artifact.ts","lineNumber":175,"sourceCode":"  return promise\n}\n\n/**\n * Validates and prepares a test attachment for serialization.\n *\n * This function ensures attachments have either `body` or `path` set (but not both), and converts `Uint8Array` bodies to base64-encoded strings for easier serialization.\n *\n * @param attachment - The attachment to validate and prepare\n *\n * @throws {TypeError} If neither `body` nor `path` is provided\n * @throws {TypeError} If both `body` and `path` are provided\n */\nexport function manageArtifactAttachment(attachment: TestAttachment): void {\n  if (attachment.body == null && !attachment.path) {\n    throw new TypeError(`Test attachment requires \"body\" or \"path\" to be set. Both are missing.`)\n  }\n  if (attachment.body && attachment.path) {\n    throw new TypeError(`Test attachment requires only one of \"body\" or \"path\" to be set. Both are specified.`)\n  }\n  if (attachment.path && attachment.bodyEncoding) {\n    throw new TypeError(`Test attachment with \"path\" should not have \"bodyEncoding\" specified.`)\n  }\n  // convert to a string so it's easier to serialise\n  if (attachment.body instanceof Uint8Array) {\n    attachment.body = encodeUint8Array(attachment.body)\n  }\n  if (attachment.body != null) {\n    attachment.bodyEncoding ??= 'base64'\n  }\n}\n","sourceCodeStart":157,"sourceCodeEnd":188,"githubUrl":"https://github.com/vitest-dev/vitest/blob/1fa9837ec26533512fdcad8baebf249771bd340a/packages/vitest/src/runtime/runner/artifact.ts#L157-L188","documentation":"Thrown by manageArtifactAttachment when a TestAttachment has both a non-null 'body' and a truthy 'path'. Vitest treats attachments as either inline content (body) or a file reference (path); specifying both is ambiguous and would cause double-handling during serialization. The guard runs for every attachment on artifacts passed to recordArtifact and on annotations that carry an attachment.","triggerScenarios":"Calling context.annotate(message, attachment) or recordArtifact(task, { attachments: [...] }) where a single attachment object sets both `body` (string/Uint8Array) and `path` (string). Also triggered by custom reporters/plugins that construct TestAttachment objects with both fields populated.","commonSituations":"Copying an attachment object literal that defaulted path and then assigning body; building attachments from a generic helper that always sets path and lets the caller override body; migrating from a reporter that stored both raw bytes and a temp file path.","solutions":["Pick exactly one source: set `body` for inline content OR `path` for a file/URL, never both.","If you have both bytes and a file, write the bytes to disk first and keep only `path`, or drop the file and keep only `body`.","Strip the unused field explicitly before constructing the attachment: `const { body, ...rest } = source; attach({ ...rest, body })`.","Add a unit test asserting your attachment factory never emits both keys."],"exampleFix":"// before\nawait ctx.annotate('screenshot', {\n  path: '/tmp/shot.png',\n  body: pngBytes,\n})\n\n// after (inline)\nawait ctx.annotate('screenshot', { body: pngBytes })\n// after (file)\nawait ctx.annotate('screenshot', { path: '/tmp/shot.png' })","handlingStrategy":"validation","validationCode":"function assertAttachment(a: Partial<TestAttachment>) {\n  if (a.body != null && a.path) {\n    throw new Error('Attachment has both body and path; pick one.')\n  }\n}\n// call before context.annotate / recordArtifact","typeGuard":"const isBodyOnly = (a: TestAttachment) => a.body != null && !a.path\nconst isPathOnly = (a: TestAttachment) => !!a.path && a.body == null","tryCatchPattern":"try {\n  await ctx.annotate(msg, attachment)\n} catch (e) {\n  if (e instanceof TypeError && /both are specified/i.test(e.message)) {\n    // strip one field and retry, or log\n  } else throw e\n}","preventionTips":["Use a single attach() helper in your codebase that enforces one of body/path.","Never spread shared attachment templates that pre-set both fields.","Add an eslint rule or unit test asserting your factory output."],"tags":["attachments","test-context","validation","artifact"],"backgroundTag":null,"analyzedSha":"1fa9837ec26533512fdcad8baebf249771bd340a","analyzedAt":"2026-08-11T16:11:39.638Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}