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

  1. Keep only `body` for inline content, or only `path` for a file reference.
  2. If you have both a file and inline bytes, pick one — usually `path` is cheaper for large content.
  3. 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

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


AI-assisted analysis of vitest-dev/vitest@d568f8ce37 (2026-08-03). Data as JSON: /data/errors/26031d3bea8701d8.json. Report an issue: GitHub.