# axe-core td-headers-attr error broken headers association - how to fix it

## TL;DR

Point every headers attribute at real th ids in the same table: the id must exist, must be on a th, and must be in the same table element. Fix typos in the ids and move stray references back inside the table. One line of why: headers attributes are how complex tables wire cells to their headers, and a dangling reference leaves the cell orphaned.

## The error, verbatim

```text
{
  "id": "td-headers-attr",
  "impact": "serious",
  "help": "Table cells using headers must reference cells in the same table",
  "nodes": [
    { "target": ["td[headers]"], "failureSummary": "Fix any of the following: The headers attribute references an element that does not exist or is not a table header" }
  ]
}
```

## Fix it step by step

### Step 1: Reproduce on one page

```bash
npx @axe-core/cli https://example.com --rules td-headers-attr --save axe-tdhead.json
```

Expected: Violations array contains td-headers-attr with the broken cell selectors.

### Step 2: List the broken references

```bash
node -e "const r=require('./axe-tdhead.json'); r.violations[0].nodes.forEach(function(n){console.log(n.target.join(' '))})"
```

Expected: Selectors for td elements with dangling headers references.

### Step 3: Find them in source

```bash
rg -n 'headers=' src --glob '*.{jsx,tsx,vue,html}' | head -20
```

Expected: Shows the headers attributes and the th ids they should point at.

### Step 4: Fix the references and re-scan

```bash
npx @axe-core/cli https://example.com --rules td-headers-attr
```

Expected: Exit code 0, 0 violations. Screen readers announce the full header chain for complex tables.

### Step 5: Gate the rule in CI

```bash
npx @axe-core/cli https://example.com --exit | tail -3
```

Expected: Non-zero exit while any violation remains; add this command to CI so the fix never regresses.

## When to use this skill

- Your axe-core report lists this exact rule id under violations
- You are clearing automated WCAG 2.1 AA failures before a release or audit
- A CI a11y gate (pa11y-ci, lighthouse CI, cypress-axe) is red because of this rule

## When NOT to use this skill

- The issue only shows up in manual screen-reader testing and axe reports zero violations for the rule
- You are doing a full manual WCAG audit, this skill covers the single automated rule only
- The page is a third-party embed you cannot edit, flag it to the vendor instead

## Compatibility

axe-core 4.8+ (rule td-headers-attr, WCAG 1.3.1). Pair with scope-attr-valid for complete table coverage. Pin the tool version in the lockfile so scans stay reproducible across machines.

## Variant phrasings

### headers attribute references element that does not exist

The failureSummary wording, same fix.

### axe complex table headers id mismatch

The usual cause: th ids renamed without updating the td references.

### axe DevTools flags the same rule

The browser extension runs the same rule engine; fix once and it clears in every runner.

## Why it happens

Complex tables with spanned or multi-level headers use headers attributes pointing at th ids, and refactors rename the th ids without updating the references. Copy-pasted table sections also duplicate th ids, so the reference resolves to the wrong table's header. Axe validates that each referenced id exists, is a th, and lives in the same table element. The same violation usually repeats on every page built from the same template, so fix the component or template once instead of patching pages. After the fix, re-scan the whole site, not just the one page, to confirm the template-level change cleared them all.

## Edge cases

- Every referenced id must be unique in the document, duplicated th ids break the association silently.
- Simple tables rarely need headers attributes at all, scope on th covers them, reserve headers for multi-level headers.
- Headers referencing th elements in a different table fail even if the id exists, keep references table-local.
- Fix every instance of the rule before moving on; a half-fixed rule across templates re-fails the next full scan.

## Provenance

Resolved from the public thread: https://vectle.com/posts/pst__WW0BjlHvDICKjRtG3QzAg
