/ ← HTML Checker Fixes / Form Markup Errors
In this guide
  1. What this guide covers, and what it does not
  2. The validator messages for forms, and what each means
  3. Structural errors: nested forms and misplaced controls
  4. Attributes that each input type allows
  5. Autocomplete, inputmode and boolean attributes
  6. The fix, step by step
  7. Where these errors come from on common platforms
  8. Questions and answers

What this guide covers, and what it does not

There are two quite different questions you can ask about a form. One is whether a person can understand and complete it: are the labels clear, are errors explained, can it be used by keyboard and screen reader? That is covered in fixing form labels. The other is whether the markup itself is valid HTML, which is what the HTML Checker reports and what this guide fixes.

The two overlap, because invalid markup often breaks accessibility. A label pointing at an id that does not exist, for instance, is both a validation error and an unlabelled field. But many form errors have nothing to do with labels: they are attributes that do not belong on a given input type, values the browser cannot interpret, or structure the HTML parser rewrites before your page even loads.

A note on the word "validation": HTML also has built-in constraint validation, the browser's own checking of required, pattern, min and type="email" fields before submission. Invalid markup is one of the main reasons that checking behaves oddly, so fixing the markup is also the first step when built-in form checking seems broken.

The validator messages for forms, and what each means

Message (shortened)What it meansEffect on the form
The for attribute of the label element must refer to a non-hidden form controlThe label's for value matches no id, or matches a hidden input or a non-form elementThe field has no programmatic label; clicking the label does nothing
Any input descendant of a label element with a for attribute must have an ID value that matches that for attributeA field is wrapped in a label whose for names a different idConflicting associations; the label may attach to another field
The label element may contain at most one button, input, meter, output, progress, select, or textarea descendantTwo fields inside one label, such as day and month selectsOnly one of the fields is labelled
Attribute maxlength not allowed on element input at this pointmaxlength used on type="number", or another attribute that the type does not supportThe attribute is ignored, so the limit you expected is not enforced
Bad value "x" for attribute type on element inputAn input type that does not exist, such as datetime or phoneThe browser falls back to a plain text box
Bad value "nope" for attribute autocomplete on element inputA made-up autocomplete valueAutofill hints are lost
Start tag form seen but an element of the same type was already openA <form> inside another formThe inner form tag is discarded; its fields submit with the outer form
Bad value "" for attribute action on element form: Must be non-emptyaction=""Works by accident (submits to the current URL) but is invalid
The first child option element of a select element with a required attribute… must have either an empty value attribute, or must have no text contentA required select whose first option is a real-looking valueThe field is always "filled", so required never triggers
Bad value "false" for attribute required on element inputA boolean attribute given the value "false"The field is still required
The element button must not appear as a descendant of the a elementA button wrapped in a link, or a link wrapped in a buttonUnpredictable clicks and keyboard behaviour
The form attribute must refer to a form elementA control's form="…" points at an id that is not a formThe control is not submitted with any form
Stray end tag inputA closing </input>Harmless, but noisy; input is a void element

The exact wording varies slightly between validator versions, but the key phrase, such as "not allowed on element input" or "must refer to", stays the same and is what you should search for.

Structural errors: nested forms and misplaced controls

Fix these first, because the HTML parser changes the document when it meets them, and the other errors may be side effects.

A form inside a form

Forms cannot be nested. When the parser sees a second <form> start tag while one is open, it ignores it, and the inner form's action and method vanish. Its fields become part of the outer form. This usually happens when a page builder puts a newsletter or search widget inside a section that is already wrapped in a form, or when a checkout theme wraps the whole page in one form.

Move the inner form outside the outer one. If the controls must sit visually inside the outer form, place the second form elsewhere in the markup and link its controls to it with the form attribute:

<form id="checkout" action="/checkout" method="post">
  <label for="email">Email</label>
  <input type="email" id="email" name="email" autocomplete="email" required>

  <label for="voucher">Voucher code</label>
  <input type="text" id="voucher" name="code" form="voucher-form">
  <button type="submit" form="voucher-form">Apply</button>

  <button type="submit">Place order</button>
</form>

<form id="voucher-form" action="/voucher" method="post"></form>

Buttons inside links, and links inside buttons

Interactive elements cannot contain other interactive elements. A "Shop now" button wrapped in a link is really a navigation link, so use an <a> styled as a button. Something that submits, opens or toggles is a <button>. Choose one; never nest them.

Buttons without an explicit type

Inside a form, a <button> with no type is a submit button. A "show password" or "add another item" button without type="button" will submit the form when pressed. The validator does not always flag a missing type, but the HTML Checker's form checks do, and it is worth setting on every button: type="submit", type="button" or, rarely, type="reset".

Void elements and closing tags

input is a void element: it has no content and no closing tag. Delete any </input>. By contrast textarea, select and button must be closed; a missing </textarea> swallows the rest of the page into the text box.

Attributes that each input type allows

Most attribute errors come from treating every input as a text box. Each type accepts only the attributes that make sense for it, and anything else is ignored by the browser, which means the behaviour you expected never happens.

AttributeAllowed onCommon mistake
maxlength, minlength, pattern, placeholder, sizetext, search, url, tel, email, password (placeholder also on number)maxlength on a number field to limit digits
min, max, stepnumber, range, date, month, week, time, datetime-localmin on a text field
multipleemail, file (and select)multiple on a text field
acceptfileOn an image upload button that is not a file input
checkedcheckbox, radioOn a select option (use selected)
requiredEvery type except hidden, range, color and the button typesOn a hidden input
readonlyText-like and date typesOn a checkbox or select, where it has no effect (use disabled, and remember disabled fields are not submitted)

Limiting digits in a number field

Since maxlength does nothing on type="number", use min and max for a numeric range. For codes that are made of digits but are not quantities, such as a card security code, a one-time passcode or a phone extension, use a text input with inputmode="numeric", which shows a numeric keypad on phones, plus pattern and maxlength:

<label for="otp">6-digit code</label>
<input type="text" id="otp" name="otp" inputmode="numeric"
       pattern="[0-9]{6}" maxlength="6" autocomplete="one-time-code">

Phone numbers belong in type="tel", never type="number": a number field drops leading zeros in some cases, rejects spaces and plus signs, and adds spinner arrows.

Types that do not exist

type="datetime" was removed from HTML; use datetime-local for a date and time without a time zone. type="phone" and type="postcode" were never valid; use tel and text with a suitable autocomplete value.

Autocomplete, inputmode and boolean attributes

Valid autocomplete values

autocomplete takes a defined list of tokens. Common ones are name, given-name, family-name, email, tel, organization, street-address, address-line1, address-level2 (town or city), postal-code, country-name, username, current-password, new-password, one-time-code and the card tokens such as cc-number. You can prefix a section name and shipping or billing, as in autocomplete="shipping postal-code". Plain on and off are also valid.

Invented values such as nope, false, disabled or new-field are invalid. They became popular as tricks to stop autofill, and browsers handle them inconsistently. If a field genuinely should not be filled from saved data, off is the valid way to say so; for a password-change form, new-password tells password managers to suggest a new password rather than fill the old one.

πŸ’‘ Correct autocomplete values are also an accessibility requirement: WCAG 2.2 success criterion 1.3.5, Identify Input Purpose, asks that fields collecting the user's own details use them. Fixing the tokens clears the validator and helps meet that criterion at the same time.

inputmode values

inputmode only accepts none, text, decimal, numeric, tel, search, email and url. Values such as number or digits are invalid and ignored.

Boolean attributes

required, disabled, checked, selected, readonly, multiple, autofocus and novalidate are boolean: their presence means true. required="false" still makes the field required, and checked="false" still ticks the box. Valid forms are the bare attribute or the attribute name as its value, required="required". To make something not required, leave the attribute out. Template code that prints disabled="{{ is_disabled }}" is a frequent source of this bug.

Required selects need an empty first option

<label for="size">Size</label>
<select id="size" name="size" required>
  <option value="">Choose a size</option>
  <option value="s">Small</option>
  <option value="m">Medium</option>
</select>

Without the empty-value first option, the select always has a value, so required can never fail and visitors who never touch the field submit whatever the first option happens to be.

The fix, step by step

Step 1

Validate every page that has a form

Forms live on more pages than people expect: contact, quote request, newsletter sign-up in the footer, search in the header, login, account, basket and checkout. Run the HTML Checker on one page of each kind, including any with a form loaded in a modal. Copy the form-related messages into a list, noting the field id or name each one mentions, so you can find the exact line in the template afterwards.

Step 2

Fix structural errors first

Deal with nested forms, buttons inside links, missing closing tags on textarea or select, and stray </input> tags before anything else. The parser rewrites the document around these, so they can create other errors further down the form. Move any inner form out and connect its controls with the form attribute, then validate again. Several later errors often disappear once the structure is sound.

Step 3

Repair label and form associations

For every error mentioning a for or form attribute, check that the value exactly matches the id of the intended control, including case. Make sure that control is not a hidden input, that only one field sits inside each label, and that the id is unique on the page. Where a field has been renamed in the template but its label has not, update both together from a single variable.

Step 4

Match attributes to each input type

For each "not allowed on element input" message, compare the attribute with the input's type using the table above. Remove attributes the type ignores, or change the type if the wrong one was chosen: tel for phone numbers, a text input with inputmode="numeric" for codes, and min and max instead of maxlength for real quantities. Replace any invalid type such as datetime with a valid one.

Step 5

Correct autocomplete and inputmode values

Replace every invented autocomplete value with the matching standard token, or with off where autofill really must be discouraged. Add tokens to fields that collect the visitor's own details, such as name, email, telephone and address, and use shipping and billing prefixes on checkout forms with two addresses. Check any inputmode value against the eight allowed keywords and correct misspellings such as number to numeric.

Step 6

Fix boolean attributes and select placeholders

Search templates for ="false" and ="true" on boolean attributes and change the template logic so the attribute is printed only when it applies. Give every required select an empty-value first option with prompt text such as "Choose a size", unless it allows multiple selections or has a visible size above one. Remove action="" from forms, or set it to the real destination URL.

Step 7

Submit the form and test autofill

Validate the pages again, then use each form for real. Submit it empty to confirm required fields are caught, submit it filled in and check on the receiving end that every field arrived with the name you expect. Use your browser's saved address and password data to confirm autofill completes the right boxes. Finally, press each non-submit button and make sure none of them submits the form by accident.

Where these errors come from on common platforms

The platform guides for WordPress and Shopify explain how to find and override the template that prints a form on each platform.

Questions and answers

1. Does fixing form markup errors improve SEO?

Not directly. Search engines do not rank pages on whether their forms validate. The value is in the form working properly: autofill completing addresses correctly, required fields being caught before submission, and every field arriving at your inbox or system. Those affect enquiries and sales far more than rankings. Correct autocomplete values and working labels also help meet WCAG 2.2, which matters if you have accessibility obligations. Treat form errors as a conversion and accessibility fix rather than a search one.

2. Why does my required field let people submit it empty?

Three common causes. The field may be a select whose first option has a real value, so it is never empty; add an empty-value placeholder option first. The form may have the novalidate attribute, or the submit button formnovalidate, which switches off the browser's own checks. Or a script may submit the form directly, bypassing built-in checking. Invalid markup, such as a nested form moving the field into a different form, can also mean the required field is not the one being checked.

3. How do I stop the browser autofilling a field?

Use autocomplete set to off for fields that should not be filled from saved data, such as a gift message or a one-off reference number. Browsers may still offer suggestions in some cases, particularly for login fields, because they prioritise helping people use password managers. For a new password on a sign-up or change-password form, use new-password instead, which tells password managers to suggest a fresh password. Avoid invented values such as nope, which are invalid and behave unpredictably across browsers.

4. Should phone numbers use type number or type tel?

Use type tel. Phone numbers are not quantities: they can start with zero, contain spaces, brackets or a plus sign, and should never show increment arrows. A number input may reject or mangle those characters, and the validator will flag attributes such as maxlength on it. Type tel shows a phone keypad on mobile devices without imposing number rules, and pairing it with autocomplete set to tel lets browsers fill the visitor's saved number. Validate the format on the server as well.

5. Is it valid to put a form inside another form if I need two submit actions?

No, forms cannot be nested, and the browser ignores the inner form's opening tag, so its fields join the outer form. If you need two different submit actions from one set of fields, give each submit button a formaction attribute pointing at a different URL. If you need a separate small form, such as a voucher code box inside a checkout, place that form elsewhere in the markup and connect its input and button to it using the form attribute with the form's id.

6. Do I need both an id and a name on every input?

They do different jobs. The name is what the server receives, so any field whose value you want submitted must have one; a field without a name is simply left out of the submission. The id is what a label, ARIA reference or script uses to find the field, so it is needed whenever an external label points at the field. Wrapping the input inside its label removes the need for an id, but most teams include both for clarity.

7. The validator error comes from a form plugin. Can I fix it?

Often, yes, through the plugin's settings rather than code. Check each field's type, the autocomplete or HTML attributes option, and whether the same form has been placed twice on the page, which causes duplicate ids. Update the plugin, since many validation issues are fixed in newer versions. If the error is in markup the plugin generates and you cannot change it, report it to the developer, and consider a different plugin if it breaks labels or submissions.

Check the markup of every form

The HTML Checker validates your page against W3C standards and reports errors and warnings covering form semantics, as well as the doctype, encoding, nesting, deprecated elements, duplicate IDs, ARIA and the language attribute.

Open HTML Checker Fixes β†’
πŸ’¬ Got a problem?
Related Guides: All HTML Checker Fixes  ·  Fixing Form Labels  ·  Fixing Duplicate id Attributes  ·  Forms AI Agents Cannot Fill  ·  Learning Hub

About aiwebpageseo

aiwebpageseo.com is a data-driven SEO and AEO (Answer Engine Optimisation) platform providing a free suite of technical SEO tools. Rather than relying on AI-theorised assumptions, the platform analyses live URL performance, delivering objective diagnostics, page speed metrics, CLS debugging, and site crawl data alongside actionable technical tutorials.