WCAG 3.3.1: Error Identification
When a form rejects what someone typed, you must tell them which field is wrong and what is wrong with it, in text. This Level A criterion is what stops a failed form from becoming a dead end — a red border with no message leaves screen reader users, colour-blind users, and anyone in a hurry stuck, with no way to find or fix the problem.
The success criterion, in full
If an input error is automatically detected, the item that is in error is identified and the error is described to the user in text.
Why error identification matters
Forms are where tasks succeed or fail: signing up, checking out, booking, paying, applying. The moment validation rejects an entry, the user needs three things — to know that something is wrong, which field is wrong, and how to fix it. If your form only turns a border red, a sighted user with full colour vision might puzzle it out. A screen reader user hears nothing change. A colour-blind user sees no change at all. Everyone else has to hunt up and down a long form guessing which field the computer objected to.
The cost is concrete: abandoned checkouts, failed applications, and support tickets. Error handling is consistently one of the weakest areas of real-world forms, and because the fix is well understood, 3.3.1 is a high-impact criterion to get right. Good error identification also helps people with cognitive disabilities, users on small screens, and anyone moving quickly — it is a usability win far beyond compliance.
What 3.3.1 actually requires
The criterion has three moving parts. Meet all of them and a rejected form becomes recoverable instead of a dead end.
1. Identify which item is in error
Point the user to the specific field that failed.
When validation rejects input, the user must be able to tell exactly which control caused the problem — not just that 'the form has errors somewhere'. The field is identified by associating its error message with the control, by moving focus to it, and (for visual users) by a clear visible marker on the field itself.
2. Describe the error in text
Say what is wrong in words, not a red border alone.
The description must be available as text. 'Email is required' or 'Enter a date as DD/MM/YYYY' tells the user both what went wrong and, ideally, how to fix it. A red outline, a colour change, or an icon with no text equivalent does not satisfy 3.3.1 because it is not perceivable to screen reader users and not perceivable to users who cannot distinguish the colour.
3. Only when the error is automatically detected
3.3.1 applies to errors your code can catch.
The criterion is scoped to input errors that are automatically detected — a missing required field, text in a number field, an email without an @, a date outside the allowed range. It does not require you to catch mistakes a machine cannot know about, such as the user typing a real but wrong street name.
Note: 3.3.1 asks you to identify and describe the error. It does not, at Level A, require you to suggest a fix — that is the job of the related criteria below, specifically 3.3.3 Error Suggestion at Level AA. But describing an error well almost always means hinting at the fix, so you often satisfy both at once.
Why colour or an icon alone fails
The most common 3.3.1 failure is signalling an error with styling but no words. It breaks in several ways:
- A red border or red text is invisible to screen reader users — there is no text for the assistive technology to read, so the error simply does not exist for them.
- Colour alone also fails 1.4.1 Use of Color: people with colour-vision deficiencies may not perceive red versus the default border, so the only cue is lost.
- An error icon with no accessible label or adjacent text is announced as nothing, or as 'graphic', telling the user neither which field nor what is wrong.
- A message that is on screen but not programmatically associated with its field (just sitting nearby in the layout) is not reliably announced when the user reaches the control.
The fix is to always pair the visual cue with a text message that names the problem, mark the field with aria-invalid="true", and connect the message to the field with aria-describedby. Use colour and icons as additional reinforcement, never as the only signal.
Two patterns: inline messages and an error summary
Two well-tested patterns satisfy 3.3.1. The most robust forms use both together.
Inline field messages
A text message sits beside each invalid control, tied to it with aria-describedby and flagged with aria-invalid. When the user focuses the field, they hear both the label and the error. This is precise and keeps the fix next to the field.
Error summary at the top
On submit, a focusable summary box lists every error as links that jump to the field. Move keyboard focus to its heading so the user lands on the list immediately. This is the GOV.UK pattern and is especially valuable on long forms.
A note on timing: validating inline as the user types can announce an error before they have finished entering a value, which is jarring with a screen reader. Prefer validating when the user leaves a field or submits, and reserve assertive announcements for genuine, settled errors.
Code examples
An accessible field in its error state
The core pattern: a real label, an error message with an id, and the field wired to it with aria-invalid and aria-describedby.
<label for="email">Email address</label>
<input
type="email"
id="email"
name="email"
autocomplete="email"
aria-invalid="true"
aria-describedby="email-error"
/>
<!-- Text message, not colour alone. Announced with the field. -->
<p id="email-error" class="field-error">
<span aria-hidden="true">⚠ </span>
Enter a valid email address, for example name@example.com
</p>A focusable error summary
On submit, render this above the form and move focus to it so the user lands on the list of problems. Each link jumps to the field.
<div role="alert" tabindex="-1" id="error-summary" class="error-summary">
<h2>There is a problem</h2>
<ul>
<li><a href="#email">Enter a valid email address</a></li>
<li><a href="#password">Password must be at least 12 characters</a></li>
</ul>
</div>
<script>
// After validation fails, send focus to the summary
document.getElementById('error-summary').focus()
</script>Announce a dynamically inserted error with a live region
When the message appears without a reload, a live region present in the DOM before the text is injected gets announced reliably.
<!-- Render this empty container up front -->
<p id="status" aria-live="assertive" class="sr-only"></p>
<script>
// Then populate it when an async submit is rejected
document.getElementById('status').textContent =
'2 fields need attention. See the messages above.'
</script>Accessible React field with error handling
A reusable field that toggles aria-invalid and only describes the field with the error id when an error is present.
function Field({ id, label, error, ...props }) {
const errorId = `${id}-error`
return (
<div>
<label htmlFor={id}>{label}</label>
<input
id={id}
aria-invalid={error ? true : undefined}
aria-describedby={error ? errorId : undefined}
{...props}
/>
{error && (
<p id={errorId} className="field-error">
<span aria-hidden="true">⚠ </span>
{error}
</p>
)}
</div>
)
}Common mistakes
- Signalling errors with a red border or red text only, with no text message describing what is wrong.
- Showing an error message visually but not associating it with the field via aria-describedby, so screen readers never announce it.
- Forgetting aria-invalid, so the field is not announced as being in an error state even when a message exists.
- Leaving aria-invalid="true" or stale describedby messages in place after the user corrects the value.
- An error icon with no accessible name or adjacent text — announced as nothing, conveying neither field nor problem.
- Inserting an error message into the page without a live region or a focus move, so a screen reader user never learns it appeared.
- Validating aggressively on every keystroke, announcing 'invalid' before the user has finished typing the value.
- A vague summary ('Please fix the errors below') with no link to, or naming of, the specific fields that failed.
How to test for 3.3.1
- 1
Submit the form with deliberate errors
Leave required fields blank, enter an email without an @, type letters in a number field. Confirm the form is rejected and that each problem produces a clear text message — not only a colour change.
- 2
Listen with a screen reader
Using NVDA, JAWS, or VoiceOver, submit an invalid form and confirm you are told that errors exist, which fields they are on, and what is wrong. Tab to each invalid field and verify the error is announced with the field.
- 3
Turn off colour perception
View the form in greyscale (or imagine red looks like grey). Every error must still be identifiable from text and shape, satisfying 1.4.1 Use of Color as well as 3.3.1.
- 4
Check the accessible state in DevTools
Inspect each invalid control's Accessibility pane: confirm aria-invalid is true and the description resolves to the visible error text, and that both clear once the value becomes valid.
- 5
Test keyboard-only recovery
Without a mouse, submit, reach the error summary or first invalid field, read the message, fix it, and resubmit successfully — all from the keyboard.
Audit a live form with the URL Accessibility Auditor and learn the manual screen reader steps in the Screen Reader Testing Guide.
Related success criteria
3.3.2 Labels or Instructions — A
The prevention side of the same problem: clear labels and instructions up front mean fewer errors to identify later. 3.3.1 handles the errors that still slip through.
1.4.1 Use of Color — A
Requires that colour is never the only way information is conveyed — so a red border alone fails here too. Pair every colour cue with text.
4.1.2 Name, Role, Value — A
Governs how state — including the invalid state set by aria-invalid — is exposed to assistive technology so the error is actually announced.
4.1.3 Status Messages — AA
Covers announcing status — such as an error count — through live regions without moving focus, the AA partner to 3.3.1's Level A baseline.
Browse every criterion in the WCAG Success Criteria hub or work through the full WCAG 2.2 checklist.
Frequently asked questions
What does WCAG 3.3.1 Error Identification require?
WCAG 3.3.1 requires that when an input error is automatically detected, the item in error is identified and the error is described to the user in text. In plain terms: if your validation rejects something, you must tell the user which field is wrong and what is wrong with it, using text — not just a red border or a colour change. It is a Level A success criterion, the most essential conformance level.
Is a red border enough to flag a form error?
No. A red border or colour change alone fails 3.3.1 because the error is not described in text, and it also fails 1.4.1 Use of Color because colour is the only thing conveying the error. A red border is fine as an additional cue, but it must be accompanied by a text message that names the problem (for example, 'Enter a valid email address') and that message must be programmatically associated with the field so a screen reader announces it.
How do aria-invalid and aria-describedby work together for errors?
Set aria-invalid="true" on a control when its value is invalid so assistive technology announces the field as being in an error state. Then point aria-describedby at the id of the element that holds the error text, so the message is announced together with the field when it receives focus. Remove aria-invalid (or set it to false) and clear the description once the value becomes valid. Together they identify the field and describe the error — exactly what 3.3.1 asks for.
Should I show errors inline, on submit, or both?
Both patterns can meet 3.3.1; what matters is that the error is identified and described in text and is reachable by keyboard and screen reader. A common accessible approach is to validate on submit, render a focusable error summary at the top of the form that links to each invalid field, and also show an inline message next to each field tied with aria-describedby. If you validate inline as the user types or leaves a field, avoid announcing an error before the user has finished — premature, rapidly-changing announcements are disorienting.
How do I make a dynamically inserted error message get announced?
If an error message appears without a page reload — for example after an asynchronous submit — wrap it in a live region so screen readers announce it. Use role="alert" (which maps to an assertive live region) for a single urgent message, or an element with aria-live="assertive" / aria-live="polite" that is present in the DOM before you inject the text. For a list of errors, moving keyboard focus to a focusable error summary heading is the most reliable way to make sure the user lands on the errors.
How is 3.3.1 different from 3.3.2, 3.3.3, and 4.1.3?
They form the form-handling family. 3.3.2 Labels or Instructions is about preventing errors up front with clear labels and hints. 3.3.1 Error Identification (Level A) covers identifying and describing an error once it happens. 3.3.3 Error Suggestion (Level AA) goes further and asks you to suggest a correction when you know one. 4.1.3 Status Messages (Level AA) covers announcing status — including error counts — without moving focus, via live regions. A well-built form satisfies all four together.