# Errors include context without PII

- **Pattern:** `ab-002281` (`saas-error-handling.error-reporting.errors-include-context-no-pii`)
- **Severity:** medium
- **Lifecycle:** active
- **Last modified:** 2026-04-18
- **Canonical URL:** https://auditbuffet.com/patterns/ab-002281
- **License:** CC-BY-4.0 — attribute to AuditBuffet Pattern Catalog (https://auditbuffet.com/patterns/ab-002281)

## Why it matters

Error events captured with no context tell you something crashed but not who, where, or what the user was doing — making reproduction difficult and prioritization impossible. Attaching PII to error context (email addresses, full names, raw form values) violates GDPR Art. 5(1)(f) (integrity and confidentiality) and Art. 25 (data minimization by design). CWE-359 (Exposure of Private Personal Information) applies when email or identity fields are attached to error events that are stored and queryable in third-party services like Sentry. User IDs (non-identifiable UUIDs) are acceptable; email addresses are not — they link errors to identifiable individuals stored in a vendor's system.

## Severity rationale

Medium because contextless errors are a productivity failure, and PII-in-errors is a GDPR data minimization violation requiring remediation before a data subject access request exposes it.

## Remediation

Set user context once after authentication using only non-identifying fields, then add action context per capture.

```ts
// After user authenticates — ID only, never email or name
Sentry.setUser({ id: session.userId })
Sentry.setTag('plan', session.planTier)
```

For individual captures:

```ts
Sentry.withScope((scope) => {
  scope.setTag('action', 'checkout.submit')
  scope.setContext('cart', { itemCount: cart.items.length }) // aggregate, not raw items
  Sentry.captureException(error)
})
```

Audit your existing Sentry user context configuration and remove any fields containing `email`, `name`, or other GDPR-covered PII.

## Detection

- **ID:** `errors-include-context-no-pii`
- **Severity:** `medium`
- **What to look for:** Examine error reporting initialization and error capture calls. Look for: (1) whether user context is attached (Sentry's `setUser()`, Bugsnag's `setUser()`, or custom tags with user ID, session ID, or role); (2) whether route/page context is attached (URL, route params, feature area); (3) whether action context is attached (what the user was doing when the error occurred); (4) critically, whether any of this context includes PII — full names, email addresses, phone numbers, passwords, or raw user input. User IDs (non-identifiable UUIDs or numeric IDs) are acceptable; email addresses are not.
- **Pass criteria:** Count all `captureException` or equivalent error reporting calls in the codebase. Pass if errors are captured with at least 1 contextual field (user ID, route, or action label) and none of the captured fields contain email addresses, full names, or other PII. A bare `captureException(error)` with no context is a fail; a `captureException(error)` with `{ userId: session.userId, route: pathname }` is a pass. Report the count: "X of Y error capture calls include contextual fields."
- **Fail criteria:** Fail if errors are captured with no context at all (pure `captureException(error)` calls everywhere). Fail if context includes PII fields (email, name, phone, raw form values). Fail if user data is passed directly from form state into error context without filtering.
- **Skip (N/A) when:** No error reporting service is configured (the `error-reporting-service` check already surfaces this). Skip this check if that check resulted in `fail` or `skip`.
- **Detail on fail:** Describe what context is missing or what PII was found (e.g., "captureException called without user/route context in 8 locations" or "Sentry setUser() includes email field — replace with userId only"). Max 500 chars.
- **Remediation:** Errors without context are debugging nightmares — you see what crashed but not who, where, or when. Errors with too much context create privacy liability.

  Set up context once at the session level:

  ```ts
  // After user authenticates
  Sentry.setUser({ id: session.userId }) // ID only, never email or name
  Sentry.setTag('plan', session.planTier)
  ```

  For individual error captures, add action context:

  ```ts
  Sentry.withScope((scope) => {
    scope.setTag('action', 'checkout.submit')
    scope.setContext('cart', { itemCount: cart.items.length }) // aggregate data, not raw items
    Sentry.captureException(error)
  })
  ```

  Review your Sentry/Bugsnag user context configuration and remove any fields containing email, name, or other PII.

---

## External references

- cwe CWE-359
- gdpr Art. 5(1)(f)
- gdpr Art. 25

Taxons: observability, privacy-consent

HTML version: https://auditbuffet.com/patterns/ab-002281
