# Error summaries that don't say which fields failed

Source: https://easeweb.dev/learn/error-summary
Topics: Forms > An error summary lists every problem (WCAG 3.3.1)
The fix: Name every field in error, and what to change, in text. Either show an error summary before the form that lists each problem as a link to its field, moves focus to itself and is backed by a message at each field; or list the problems in the existing banner; or put a message at each field and move focus to the first one.
Test with: keyboard, screen reader, zoom
References: https://www.w3.org/WAI/WCAG22/Understanding/error-identification.html https://www.w3.org/WAI/WCAG22/Techniques/general/G139 https://www.w3.org/WAI/WCAG22/Techniques/aria/ARIA21 https://www.w3.org/WAI/tutorials/forms/notifications/ https://design-system.service.gov.uk/components/error-summary/

This is a recreated teaching example, not a finding about a named client. The code is a starting pattern: test it in your own product. No completed assistive-technology test is claimed here.

## What happens

A pottery studio's sign-up form asks for a full name, an email address and a postcode, with a "Sign up" button at the bottom. A visitor types their name, mistypes their email as "alex@" and leaves the postcode empty. They press "Sign up". A banner appears above the form: "Something went wrong. Please check the form." The email and postcode fields get red borders. Focus stays on the button. A sighted mouse user can scan for red; a screen reader user hears nothing at all, and on a phone the banner is already scrolled out of view.

## Why it fails

3.3.1 asks that when an input error is detected automatically, the item in error is identified and the error is described to the user in text. The banner is text, but it identifies nothing: it doesn't say which fields failed or why. The fields are identified only by a border colour, which is not text, and their markup doesn't mark them invalid or carry a message. So nothing in text tells anyone which items are in error.

## Who is affected

The longer the form, the worse this gets. On a short form the red borders may sit in one view; on a checkout or an application form they are spread over several screens, and every person who can't take them all in at once has to search.

## What automation and AI miss

Scanners load the empty form and see labelled fields, so it passes; the failure only exists after a failed submission. A test that does submit finds a banner with text in it, which looks like an error message. Whether that text names the failed fields is a judgement no rule makes. An AI fix often adds `role="alert"` to the banner: screen readers then announce "Please check the form", which tells them something failed but still not what.

## Before: a general banner and red borders

```html
<div class="banner">Something went wrong. Please check the form.</div>

<form novalidate>
  <label for="name">Full name</label>
  <input id="name" name="name" autocomplete="name" value="Alex Morgan">

  <label for="email">Email address</label>
  <input id="email" name="email" type="email" autocomplete="email"
    class="invalid" value="alex@">

  <label for="postcode">Postcode</label>
  <input id="postcode" name="postcode" autocomplete="postal-code" class="invalid">

  <button>Sign up</button>
</form>
```

The banner says something failed but not what. The `invalid` class only paints a red border, so the two fields in error are identified by colour and nothing else.

## After: an error summary that links to each field

This is the page as the server, or the script, renders it after the failed submission:

```html
<div id="error-summary" tabindex="-1" aria-labelledby="error-summary-title">
  <h2 id="error-summary-title">There are 2 problems</h2>
  <ul class="list-inside list-disc">
    <li><a href="#email">Enter an email address, like name@example.com</a></li>
    <li><a href="#postcode">Enter a postcode</a></li>
  </ul>
</div>

<form novalidate>
  <label for="name">Full name</label>
  <input id="name" name="name" autocomplete="name" value="Alex Morgan">

  <label for="email">Email address</label>
  <input id="email" name="email" type="email" autocomplete="email" value="alex@"
    aria-invalid="true" aria-describedby="email-error">
  <p id="email-error">Error: Enter an email address, like name@example.com</p>

  <label for="postcode">Postcode</label>
  <input id="postcode" name="postcode" autocomplete="postal-code"
    aria-invalid="true" aria-describedby="postcode-error">
  <p id="postcode-error">Error: Enter a postcode</p>

  <button>Sign up</button>
</form>
```

```js
// Run once, after the failed submission has rendered.
document.getElementById('error-summary').focus();
```

Focus moves to the summary, so a screen reader reads its heading straight away and keyboard users start from the list. Each link names the problem in the words of the field's label and goes to that field: a link to an input's id moves focus into it. The message at each field says the same thing again, so people who reach a field by Tab, or who have scrolled down, still hear and see what to change.

## After: name the problems in the existing banner

```html
<div class="banner">
  <p>We couldn't sign you up. Please correct:</p>
  <ul class="list-inside list-disc">
    <li>Email address: enter an email address, like name@example.com</li>
    <li>Postcode: enter a postcode</li>
  </ul>
</div>
```

This is the smallest change: the form stays as it was and only the banner's text changes. Each failed field is now named in text, with what to change, so the page passes 3.3.1. But focus still stays on the button and the fields still carry only a red border. Screen reader users aren't told the banner changed, and everyone has to go back and find each field. Use it as a first step while the template that renders the full summary is changed.

## After: a message at each field, and focus on the first

```html
<label for="email">Email address</label>
<input id="email" name="email" type="email" autocomplete="email" value="alex@"
  aria-invalid="true" aria-describedby="email-error">
<p id="email-error">Error: Enter an email address, like name@example.com</p>

<label for="postcode">Postcode</label>
<input id="postcode" name="postcode" autocomplete="postal-code"
  aria-invalid="true" aria-describedby="postcode-error">
<p id="postcode-error">Error: Enter a postcode</p>
```

```js
// After a failed submission, take people to the first problem.
document.querySelector('[aria-invalid="true"]')?.focus();
```

Each failed field carries its own message, and the screen reader reads it with the label when focus arrives. That passes 3.3.1 and puts people at the first problem at once. What it doesn't say is how many problems there are. That's fine on a form of two or three fields that fits on one screen; on a longer form people find the next error only when they Tab into it.

## Which option to use

All three name each failed field in text and pass 3.3.1. Use the error summary with linked messages at each field: it is the only one that tells people at once how many problems there are, takes them to each, and still explains each field when they arrive, and the other two are each missing one of those. Naming the problems in the banner is a quick patch for a template you can't change yet. Focusing the first field is enough on its own only on a form so short that every field is in view.

## Implementation decisions

**Render the summary after the submission, not before.** Don't keep an empty summary on the page, and don't update it on every keystroke. Build it once from the same list of errors the server, or your client-side check, returns, so the summary and the field messages can't disagree.

**Move focus once.** Focus the summary after the failed response has rendered, and not again while people are fixing things. Don't add `role="alert"` to a summary you also focus: screen readers would read it twice.

**Tell people in the page title.** On a full page reload, start the `<title>` with "Error:" ("Error: Sign up | Pottery studio"). Screen readers read the title first, so people learn the outcome before anything else.

**Link to the field, and keep its label in view.** A link to an input's id focuses the input, but some browsers scroll so the label sits above the top of the window, or under a sticky header. Give the fields `scroll-margin-top` larger than any sticky header, or handle the click in script and scroll the label into view. For a group of radio buttons, link to the first radio button and put the message inside the `fieldset`.

**Say "Error:" in the field message.** The word, visible or in visually hidden text, tells people who can't see the red that this is an error and not a hint. It also tells screen reader users why the field description has changed.

**Keep the values.** Return the entered values, except passwords and card numbers, so people correct the mistake rather than fill in the form again. For the wording of each message, see the guide on form errors.

## Verify the fix

1. Using only the keyboard, submit the form with three mistakes, and check that from wherever focus lands you can find each problem and reach its field.
2. Check every failed field is named in text with what to change, and that no field is marked only by its border colour.
3. With a screen reader, submit with mistakes and check you learn the form failed and what the first problem is without searching the page. Then move to each failed field and check its message is read with its label.
4. Correct one field and send again, and check its error is gone, the others remain, and any count in the summary has changed.
5. At 400% zoom, where the page reflows to one column, and on a narrow screen, check the error text wraps and stays readable, and that a field reached from an error isn't hidden under a sticky header.

Record the OS, browser and assistive-technology versions, the build, the date, and the actual result of each step. Repeat when fields are added or the shared form component changes.

## Limits

This is a starting pattern, not a guarantee for every application. It covers how people learn which fields failed after a submission, not how each message is worded, nor errors in single-field searches, where focusing the field is enough. Forms built by third-party widgets, such as payment fields in an iframe, report errors their own way and need the same check. Test the complete journey with your target assistive technology.
