vitest-dev/vitest · error · TypeError

Test attachment requires "body" or "path" to be set. Both ar

Error message

Test attachment requires "body" or "path" to be set. Both are missing.

What it means

`manageArtifactAttachment` (`artifact.ts:172`) validates every attachment carries a payload: at least one of `body` (inline string/`Uint8Array`) or `path` (a file reference) must be set. An attachment with neither — e.g. `{ name: 'log' }` — is rejected with this `TypeError` before serialization, since there'd be nothing to attach.

Source

Thrown at packages/vitest/src/runtime/runner/artifact.ts:172

    test.promises = []
  }
  test.promises.push(promise)
  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. Set `attachment.body` (string or `Uint8Array`) OR `attachment.path` (absolute/relative filepath).
  2. If the payload is optional, skip pushing that attachment entirely instead of pushing an empty one.
  3. Validate attachments before calling `recordArtifact` (see validationCode).

Example fix

// before
recordArtifact(task, {
  type: 'report',
  attachments: [{ name: 'log' }],
})

// after
recordArtifact(task, {
  type: 'report',
  attachments: [{ name: 'log', path: '/tmp/run.log' }],
})
Defensive patterns

Strategy: validation

Validate before calling

// Validate each attachment has a payload before recording.
function validateAttachments(attachments: TestAttachment[]) {
  for (const a of attachments) {
    if (a.body == null && !a.path) {
      throw new TypeError(`Attachment "${a.name ?? '<unnamed>'}" needs body or path`)
    }
  }
}
validateAttachments(artifact.attachments ?? [])
await recordArtifact(task, artifact)

Type guard

function hasAttachmentPayload(a: { body?: unknown; path?: string }): boolean {
  return a.body != null || Boolean(a.path)
}

Try / catch

try {
  await recordArtifact(task, artifact)
} catch (e) {
  if (e instanceof TypeError && /requires "body" or "path"/.test(e.message)) {
    // add a body or path to each empty attachment
  } else throw e
}

Prevention

When it happens

Trigger: `recordArtifact(task, { type: '...', attachments: [{}] })`; `attachments: [{ name: 'x', contentType: 'text/plain' }]` with no body/path; building attachments programmatically and ending up with empty objects.

Common situations: Conditionally populating attachment fields and forgetting the payload; copy-paste leaving body/path off; an attachment builder that returns `{}` on a missing file.

Related errors


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