vitest-dev/vitest · error · TypeError
Test attachment requires only one of "body" or "path" to be
Error message
Test attachment requires only one of "body" or "path" to be set. Both are specified.
What it means
An attachment must specify exactly ONE payload source — `body` (inline) or `path` (file reference) — not both. `manageArtifactAttachment` (`artifact.ts:175`) rejects attachments carrying both as a `TypeError`, because the reporter wouldn't know which to serialize and the two could conflict.
Source
Thrown at packages/vitest/src/runtime/runner/artifact.ts:175
return promise
}
/**
* Validates and prepares a test attachment for serialization.
*
* This function ensures attachments have either `body` or `path` set (but not both), and converts `Uint8Array` bodies to base64-encoded strings for easier serialization.
*
* @param attachment - The attachment to validate and prepare
*
* @throws {TypeError} If neither `body` nor `path` is provided
* @throws {TypeError} If both `body` and `path` are provided
*/
export function manageArtifactAttachment(attachment: TestAttachment): void {
if (attachment.body == null && !attachment.path) {
throw new TypeError(`Test attachment requires "body" or "path" to be set. Both are missing.`)
}
if (attachment.body && attachment.path) {
throw new TypeError(`Test attachment requires only one of "body" or "path" to be set. Both are specified.`)
}
if (attachment.path && attachment.bodyEncoding) {
throw new TypeError(`Test attachment with "path" should not have "bodyEncoding" specified.`)
}
// convert to a string so it's easier to serialise
if (attachment.body instanceof Uint8Array) {
attachment.body = encodeUint8Array(attachment.body)
}
if (attachment.body != null) {
attachment.bodyEncoding ??= 'base64'
}
}
View on GitHub (pinned to d568f8ce37)
Solutions
- Keep only `body` for inline content, or only `path` for a file reference.
- If you have both a file and inline bytes, pick one — usually `path` is cheaper for large content.
- Validate attachments before calling `recordArtifact` (see validationCode).
Example fix
// before
recordArtifact(task, {
type: 'report',
attachments: [{ name: 'out', body: 'data', path: '/tmp/f' }],
})
// after — pick one
recordArtifact(task, {
type: 'report',
attachments: [{ name: 'out', body: 'data' }],
}) Defensive patterns
Strategy: validation
Validate before calling
// Ensure body and path are mutually exclusive.
function validateAttachments(attachments: TestAttachment[]) {
for (const a of attachments) {
if (a.body != null && a.path) {
throw new TypeError(`Attachment "${a.name ?? '<unnamed>'}" has both body and path`)
}
}
}
validateAttachments(artifact.attachments ?? [])
await recordArtifact(task, artifact) Type guard
function hasExclusivePayload(a: { body?: unknown; path?: string }): boolean {
const hasBody = a.body != null
const hasPath = Boolean(a.path)
return (hasBody || hasPath) && !(hasBody && hasPath)
} Try / catch
try {
await recordArtifact(task, artifact)
} catch (e) {
if (e instanceof TypeError && /only one of "body" or "path"/.test(e.message)) {
// drop one of body/path and retry
} else throw e
} Prevention
- Choose ONE payload source per attachment — body for inline, path for files.
- For large content, prefer path to avoid bloating the serialized artifact.
- Validate the body/path exclusivity in a builder before calling recordArtifact.
When it happens
Trigger: `recordArtifact(task, { type: '...', attachments: [{ body: 'data', path: '/tmp/f' }] })`; a builder that defensively sets every field.
Common situations: Copy-paste from an example/template that had both fields; a 'set everything' defensive pattern; merging two attachment configs.
Related errors
- Test attachment requires "body" or "path" to be set. Both ar
- `bench.from()` requires a name (string or named function) as
- `bench.from()` expects a string path or a function returning
- Test attachment with "path" should not have "bodyEncoding" s
- bitLength is required
AI-assisted analysis of vitest-dev/vitest@d568f8ce37 (2026-08-03).
Data as JSON: /data/errors/26031d3bea8701d8.json.
Report an issue: GitHub.