HTML Guides
Learn how to identify and fix common HTML validation errors flagged by the W3C Validator — so your pages are standards-compliant and render correctly across every browser. Also check our Accessibility Guides.
The maxlength attribute provides built-in client-side validation that caps the number of characters a user can type into a field. Browsers enforce this by preventing further input once the limit is reached. However, this behavior only makes sense for input types that accept arbitrary text strings. Input types like number, date, range, and checkbox have their own value formats and constraints — a number input’s value is controlled by min, max, and step, not by character count.
When you place maxlength on an unsupported input type, browsers will ignore the attribute. This means it provides no actual validation while giving a false sense of security. It also produces invalid HTML, which can cause issues with assistive technologies that may try to interpret the attribute and relay incorrect information to users. Keeping your markup valid ensures predictable behavior across browsers and a better experience for all users.
How to fix it
- Remove maxlength from any <input> whose type is not email, password, search, tel, text, or url.
- Use the correct constraint attributes for the input type in question. For number inputs, use min and max. For date inputs, use min and max with date strings.
- If you genuinely need character-length validation, consider whether a text-based input type is more appropriate for your use case, or implement the constraint in JavaScript.
Examples
❌ Invalid: maxlength on a number input
<label for="quantity">Quantity</label>
<input type="number" id="quantity" name="quantity" maxlength="3">
The number type does not support maxlength. Browsers will ignore it, and the HTML is invalid.
✅ Fixed: using min and max for a number input
<label for="quantity">Quantity</label>
<input type="number" id="quantity" name="quantity" min="0" max="999">
If the goal was to limit the value to three digits, min and max are the correct constraints.
❌ Invalid: maxlength on a date input
<label for="start-date">Start date</label>
<input type="date" id="start-date" name="start-date" maxlength="10">
Date inputs have a browser-provided date picker, and their values are always in YYYY-MM-DD format. The maxlength attribute has no effect here.
✅ Fixed: using min and max for a date input
<label for="start-date">Start date</label>
<input type="date" id="start-date" name="start-date" min="2020-01-01" max="2030-12-31">
❌ Invalid: maxlength on a checkbox
<label>
<input type="checkbox" name="agree" maxlength="1"> I agree
</label>
A checkbox is a boolean toggle — character length is meaningless here.
✅ Fixed: removing the invalid attribute
<label>
<input type="checkbox" name="agree"> I agree
</label>
✅ Valid: maxlength on supported text-based types
<label for="username">Username</label>
<input type="text" id="username" name="username" maxlength="30">
<label for="user-email">Email</label>
<input type="email" id="user-email" name="email" maxlength="254">
<label for="user-phone">Phone</label>
<input type="tel" id="user-phone" name="phone" maxlength="15">
<label for="site-url">Website</label>
<input type="url" id="site-url" name="website" maxlength="2048">
<label for="user-pass">Password</label>
<input type="password" id="user-pass" name="password" maxlength="128">
<label for="query">Search</label>
<input type="search" id="query" name="q" maxlength="100">
All six of these input types support maxlength because they accept free-form text where limiting character count is meaningful.
The minlength attribute defines the minimum number of characters (as UTF-16 code units) that a user can enter into a text-based input field. It provides built-in client-side validation without requiring JavaScript. However, it only makes sense on input types where the user is typing free-form text. For input types like number, date, color, range, or checkbox, the concept of a minimum character length doesn’t apply — these inputs have their own constrained value formats or use other attributes like min and max for validation.
When the W3C validator encounters minlength on an unsupported input type, it flags the attribute as invalid. Browsers will typically ignore the attribute silently, meaning your intended validation won’t actually work. This can lead to a false sense of security where you believe the input is being validated when it isn’t.
The minlength value must be a non-negative integer (0 or higher) and must be less than or equal to maxlength if both are specified. The minlength attribute also works on <textarea> elements.
Examples
❌ Incorrect: minlength on a number input
The number input type doesn’t support minlength. If you want to enforce a minimum value, use the min attribute instead.
<label for="age">Enter your age</label>
<input type="number" minlength="1" id="age">
✅ Fixed: Using min for number inputs
<label for="age">Enter your age</label>
<input type="number" min="1" id="age">
❌ Incorrect: minlength on a date input
<label for="start-date">Start date</label>
<input type="date" minlength="10" id="start-date">
✅ Fixed: Remove minlength from date inputs
Date inputs have a browser-controlled format, so character length constraints don’t apply. Use min and max to constrain the date range.
<label for="start-date">Start date</label>
<input type="date" min="2024-01-01" id="start-date">
✅ Correct: minlength on supported input types
Here are valid uses of minlength across all supported input types:
<label for="username">Username (at least 3 characters)</label>
<input type="text" minlength="3" id="username">
<label for="email">Email</label>
<input type="email" minlength="5" id="email">
<label for="pass">Password (at least 8 characters)</label>
<input type="password" minlength="8" id="pass">
<label for="phone">Phone number</label>
<input type="tel" minlength="7" id="phone">
<label for="query">Search</label>
<input type="search" minlength="2" id="query">
<label for="website">Website URL</label>
<input type="url" minlength="10" id="website">
✅ Correct: minlength with maxlength on a textarea
<label for="bio">Bio (between 10 and 200 characters)</label>
<textarea minlength="10" maxlength="200" id="bio"></textarea>
Quick Reference
| Input Type | Supports minlength? | Alternative |
|---|---|---|
| text, email, password, search, tel, url | ✅ Yes | — |
| textarea | ✅ Yes | — |
| number, range | ❌ No | Use min / max |
| date, datetime-local, time, month, week | ❌ No | Use min / max |
| checkbox, radio | ❌ No | Use required |
| file | ❌ No | Validate with JavaScript |
| color, hidden | ❌ No | Not applicable |
This error is misleading at first glance because the <meta> tag in question is often perfectly well-formed. The real problem is usually above the <meta> tag — an element that doesn’t belong in <head> (such as <img>, <div>, <p>, or other flow content) has been placed there. When the HTML parser encounters such an element inside <head>, it implicitly closes the <head> and opens the <body>. From that point on, any subsequent <meta> tags are now technically inside the <body>, where the name attribute on <meta> is not permitted.
In other cases, the error can also occur when a <meta name="..."> tag is explicitly placed inside <body>, or when a typo or malformed tag earlier in the document breaks the expected document structure.
This matters for several reasons. Search engines and social media platforms rely on <meta> tags being in the <head> to extract page descriptions, Open Graph data, and other metadata. If the document structure is broken and <meta> tags end up in the <body>, this metadata may be ignored entirely. Additionally, elements like <img> inside <head> won’t render as expected, and the overall document structure will be invalid, potentially causing unpredictable behavior across browsers.
How to fix it
- Look above the flagged <meta> tag. Find any element in the <head> that doesn’t belong there — common culprits include <img>, <div>, <span>, <p>, <a>, or <section>.
- Move the offending element into the <body> where it belongs.
- If the <meta> tag itself is in the <body>, move it into the <head>.
- Check for malformed tags above the <meta> — an unclosed tag or a typo can break the parser’s understanding of the document structure.
Only certain elements are allowed inside <head>: <title>, <meta>, <link>, <style>, <script>, <noscript>, <base>, and <template>.
Examples
An invalid element in <head> breaks the context
The <img> tag is not allowed inside <head>. The parser implicitly closes <head> when it encounters it, so the <meta> tag that follows ends up in <body>:
<!DOCTYPE html>
<html lang="en">
<head>
<title>My Page</title>
<img src="photo.jpg" alt="A smiling cat">
<meta name="description" content="A page about cats">
</head>
<body>
<p>Welcome!</p>
</body>
</html>
Move the <img> into the <body> to fix the issue:
<!DOCTYPE html>
<html lang="en">
<head>
<title>My Page</title>
<meta name="description" content="A page about cats">
</head>
<body>
<img src="photo.jpg" alt="A smiling cat">
<p>Welcome!</p>
</body>
</html>
A <meta> tag accidentally placed in <body>
<!DOCTYPE html>
<html lang="en">
<head>
<title>My Page</title>
</head>
<body>
<meta name="author" content="Jane Doe">
<p>Hello world</p>
</body>
</html>
Move the <meta> tag into <head>:
<!DOCTYPE html>
<html lang="en">
<head>
<title>My Page</title>
<meta name="author" content="Jane Doe">
</head>
<body>
<p>Hello world</p>
</body>
</html>
A malformed tag disrupts the <head>
A missing closing > on a <link> tag can confuse the parser, causing subsequent elements to be misinterpreted:
<!DOCTYPE html>
<html lang="en">
<head>
<title>My Page</title>
<link rel="stylesheet" href="style.css"
<meta name="viewport" content="width=device-width, initial-scale=1">
</head>
<body>
<p>Content</p>
</body>
</html>
Close the <link> tag properly:
<!DOCTYPE html>
<html lang="en">
<head>
<title>My Page</title>
<link rel="stylesheet" href="style.css">
<meta name="viewport" content="width=device-width, initial-scale=1">
</head>
<body>
<p>Content</p>
</body>
</html>
When the HTML parser encounters a < character inside an opening tag, it doesn’t treat it as the start of a new tag — instead, it tries to interpret it as an attribute name. Since < is not a valid attribute name, the W3C validator raises this error. The browser may still render the page, but the behavior is undefined and can vary across different browsers, potentially leading to broken markup or elements that don’t display correctly.
This issue most commonly occurs in a few scenarios:
- Accidental keystrokes — a stray < typed while editing attributes.
- Copy-paste artifacts — fragments of other tags getting pasted into the middle of an element.
- Misplaced angle brackets — attempting to nest or close tags incorrectly, such as adding < before /> in a self-closing tag.
- Template or code generation errors — dynamic HTML output that incorrectly injects < into attribute positions.
Because this is a syntax-level problem, it can cause cascading parse errors. The parser may misinterpret everything after the stray < until it finds a matching >, which can swallow subsequent elements or attributes and produce unexpected rendering results.
How to Fix It
- Open the file referenced by the validator error and go to the indicated line number.
- Look inside the opening tag of the flagged element for a < character that doesn’t belong.
- Remove the stray < character.
- If the < was meant to represent a literal less-than sign in an attribute value, replace it with the HTML entity <.
Examples
Stray < before the closing slash
<!-- ❌ Stray "<" before the self-closing slash -->
<img src="photo.jpg" alt="smiling cat" < />
<!-- ✅ Fixed: removed the stray "<" -->
<img src="photo.jpg" alt="smiling cat" />
Stray < between attributes
<!-- ❌ Accidental "<" between attributes -->
<a href="/about" < class="nav-link">About</a>
<!-- ✅ Fixed: removed the stray "<" -->
<a href="/about" class="nav-link">About</a>
Fragment of another tag pasted inside an element
<!-- ❌ Leftover "<span" pasted inside the div's opening tag -->
<div class="card" <span>
<p>Hello world</p>
</div>
<!-- ✅ Fixed: removed the pasted fragment -->
<div class="card">
<p>Hello world</p>
</div>
Literal < intended in an attribute value
If you actually need a less-than sign inside an attribute value — for example, in a title or data-* attribute — use the < entity instead of a raw <.
<!-- ❌ Raw "<" in an attribute value can cause parsing issues -->
<span title="x < 10">Threshold</span>
<!-- ✅ Fixed: use the HTML entity -->
<span title="x < 10">Threshold</span>
The ontouchstart attribute is an inline event handler that some developers use to detect when a user touches an element on a touchscreen device. However, unlike standard event handler attributes such as onclick, onmousedown, or onkeydown, the ontouchstart attribute is not defined in the HTML specification. The W3C validator flags it because only attributes explicitly listed in the spec are allowed on a given element.
The touchstart event itself is a legitimate event defined in the Touch Events specification — it’s the inline HTML attribute form (ontouchstart) that isn’t recognized as a valid attribute by the HTML standard. This distinction is important: you can absolutely listen for touchstart events, just not via an inline attribute on an HTML element.
Why This Is a Problem
- Standards compliance: Using non-standard attributes produces invalid HTML, which can cause issues with automated testing, accessibility audits, and content management pipelines that enforce validation.
- Accessibility: Relying solely on touch events excludes users who navigate with a keyboard, mouse, or assistive technology. Touch-only handlers can make interactive elements completely inaccessible to non-touch users.
- Maintainability: Inline event handlers mix behavior with markup, making code harder to maintain and debug compared to centralized JavaScript event listeners.
- Browser inconsistencies: While most mobile browsers support the touchstart event, the inline attribute form is not guaranteed to work consistently across all environments.
How to Fix It
- Remove the inline ontouchstart attribute from your HTML element.
- Use addEventListener in JavaScript to attach the touchstart event listener programmatically.
- Consider also handling click or pointerdown events so the interaction works for all input types — mouse, keyboard, touch, and stylus. The Pointer Events API (pointerdown, pointerup, etc.) is a modern, unified approach that covers mouse, touch, and pen input in a single event model.
Examples
❌ Invalid: Inline ontouchstart attribute
<div ontouchstart="handleTouch()">Tap me</div>
This triggers the validation error because ontouchstart is not a recognized attribute in the HTML specification.
✅ Valid: Using addEventListener for touchstart
<div id="touch-target">Tap me</div>
<script>
document.getElementById("touch-target").addEventListener("touchstart", function(event) {
// Handle touch interaction
});
</script>
The touchstart event is attached through JavaScript, keeping the HTML valid and clean.
✅ Valid: Using Pointer Events for cross-input support
<button id="action-btn" type="button">Tap or click me</button>
<script>
document.getElementById("action-btn").addEventListener("pointerdown", function(event) {
// Handles mouse, touch, and pen input
});
</script>
Using pointerdown instead of touchstart provides a single handler that works across all input types. Note also the use of a <button> element, which is natively focusable and accessible, making it a better choice than a <div> for interactive elements.
✅ Valid: Supporting both touch and non-touch with separate listeners
<div id="interactive-area" role="button" tabindex="0">Interact with me</div>
<script>
var el = document.getElementById("interactive-area");
el.addEventListener("touchstart", function(event) {
// Handle touch-specific interaction
});
el.addEventListener("click", function(event) {
// Handle mouse and keyboard interaction
});
</script>
If you must use a <div> as an interactive element, add role="button" and tabindex="0" for accessibility, and attach both touchstart and click listeners to cover all input methods.
The pattern attribute provides a powerful way to add client-side form validation directly in HTML without relying on JavaScript. It accepts a regular expression that the browser uses to validate user input before the form is submitted. However, the HTML specification restricts pattern to input types where the user enters free-form text. Input types like number, date, range, color, and checkbox have their own built-in validation mechanisms (such as min, max, and step), so applying a regex pattern to them is meaningless and invalid.
When you add pattern to an unsupported input type, browsers will simply ignore it. This means you might think you have validation in place when you actually don’t, which can lead to unexpected invalid data being submitted. Removing the invalid attribute also keeps your markup clean and standards-compliant, which benefits accessibility tools and future browser behavior.
Why certain types don’t support pattern
- number — Values are constrained by min, max, and step. The browser enforces numeric input natively.
- date, time, datetime-local, month, week — These use date/time pickers with their own format and range constraints.
- range — A slider control already constrained by min, max, and step.
- checkbox, radio — These are toggled on/off or selected from a group; a regex pattern doesn’t apply.
- file — File selection is handled by the OS file picker; use the accept attribute instead.
- color — Uses a color picker with a fixed hex format.
- hidden — Not user-editable, so client-side validation is irrelevant.
How to fix it
- Remove the pattern attribute if the input type already provides sufficient validation through its native controls.
- Change the input type to one of the six supported types (email, password, search, tel, text, or url) if you genuinely need regex-based validation.
- Use alternative attributes like min, max, step, or accept that are designed for the specific input type.
- Use JavaScript validation if you need custom validation logic that goes beyond what native attributes offer.
Examples
❌ Incorrect: pattern on a number input
<label for="qty">Quantity (multiples of 5):</label>
<input type="number" id="qty" name="qty" pattern="[0-9]+" min="0" max="100">
The pattern attribute is not allowed on type="number". Since min, max, and step already handle numeric constraints, pattern is unnecessary here.
✅ Correct: using step instead of pattern for number validation
<label for="qty">Quantity (multiples of 5):</label>
<input type="number" id="qty" name="qty" min="0" max="100" step="5">
❌ Incorrect: pattern on a date input
<label for="dob">Date of birth:</label>
<input type="date" id="dob" name="dob" pattern="\d{4}-\d{2}-\d{2}">
The date input type already enforces a date format through its native picker, so pattern is invalid here.
✅ Correct: removing pattern from date input
<label for="dob">Date of birth:</label>
<input type="date" id="dob" name="dob" min="1900-01-01" max="2025-12-31">
❌ Incorrect: pattern on a checkbox input
<label>
<input type="checkbox" name="agree" pattern=".+"> I agree to the terms
</label>
✅ Correct: using required instead of pattern for checkbox
<label>
<input type="checkbox" name="agree" required> I agree to the terms
</label>
✅ Correct: pattern on a supported text input
<label for="zip">ZIP code:</label>
<input type="text" id="zip" name="zip" pattern="[0-9]{5}" title="Five digit ZIP code" required>
When using pattern on a supported input type, always include a title attribute that describes the expected format. Browsers display the title text as part of the validation error message, helping users understand what input is expected.
The placeholder attribute provides a short hint describing the expected value of an input field. This hint is displayed inside the control as light, greyed-out text when the field is empty and loses focus. It only makes sense on input types that present a visible text entry area where the user types characters directly. Input types like checkbox, radio, range, color, file, hidden, date, datetime-local, month, week, time, and image either don’t display a text field at all or use a specialized UI widget (like a date picker or file selector), so the browser has nowhere to render placeholder text.
Using placeholder on an unsupported input type violates the HTML specification as defined by WHATWG. While browsers will typically just ignore the invalid attribute, it signals a likely mistake in your markup — perhaps the input type is wrong, or the hint should be conveyed differently (e.g., via a <label> or adjacent text). Keeping your HTML valid also improves maintainability, helps assistive technologies parse your page correctly, and prevents unexpected behavior if future browser versions handle invalid attributes differently.
It’s also worth noting that even on supported input types, placeholder should not be used as a replacement for <label>. Placeholder text disappears as soon as the user starts typing, which can cause usability and accessibility issues. Always pair your inputs with a proper <label> element.
How to fix it
- Remove the placeholder attribute if it’s on an input type that doesn’t support it.
- Change the input type to one that supports placeholder if the current type is incorrect.
- Use a <label> or visible helper text to convey the hint instead, especially for non-text input types.
Examples
Invalid: placeholder on a hidden input
A hidden input is never visible to the user, so a placeholder serves no purpose.
<input type="hidden" name="token" placeholder="Session token">
Fixed: Remove the placeholder attribute.
<input type="hidden" name="token">
Invalid: placeholder on a checkbox
Checkboxes don’t have a text entry area, so there’s nowhere for placeholder text to appear.
<label>
<input type="checkbox" name="agree" placeholder="Check to agree"> I agree
</label>
Fixed: Remove the placeholder and rely on the label text to convey the hint.
<label>
<input type="checkbox" name="agree"> I agree to the terms
</label>
Invalid: placeholder on a date input
Date inputs use a browser-provided date picker widget, not a free-text field.
<label for="birthday">Birthday</label>
<input type="date" id="birthday" name="birthday" placeholder="YYYY-MM-DD">
Fixed: Remove the placeholder. If you need to show a format hint, use a separate element.
<label for="birthday">Birthday</label>
<input type="date" id="birthday" name="birthday">
<small>Format: YYYY-MM-DD</small>
Invalid: placeholder on a file input
<label for="upload">Upload</label>
<input type="file" id="upload" name="upload" placeholder="Choose a file">
Fixed: Remove the placeholder. The browser provides its own label for file inputs (e.g., “No file chosen”).
<label for="upload">Upload</label>
<input type="file" id="upload" name="upload">
Valid: placeholder on supported input types
These are all valid uses of the placeholder attribute:
<label for="email">Email</label>
<input type="email" id="email" name="email" placeholder="you@example.com">
<label for="phone">Phone</label>
<input type="tel" id="phone" name="phone" placeholder="+1 (555) 123-4567">
<label for="site">Website</label>
<input type="url" id="site" name="site" placeholder="https://example.com">
<label for="query">Search</label>
<input type="search" id="query" name="query" placeholder="Search articles…">
<label for="qty">Quantity</label>
<input type="number" id="qty" name="qty" placeholder="1">
<label for="pw">Password</label>
<input type="password" id="pw" name="pw" placeholder="Enter your password">
The HTML specification restricts the readonly attribute to input types where the user would normally type or select a textual/numeric/date value. The idea is straightforward: readonly means “you can see and select this value, but you can’t edit it.” That concept only makes sense for fields that contain editable text or structured data like dates and numbers. For controls like checkboxes, radio buttons, color pickers, file selectors, and range sliders, the interaction model is fundamentally different — there’s no text to make “read-only.”
The full list of input types that support readonly is:
- text
- search
- url
- tel
- password
- date
- month
- week
- time
- datetime-local
- number
The readonly attribute is not valid on these types: checkbox, radio, range, color, file, hidden, button, submit, reset, and image.
Why this matters
Standards compliance: Browsers are not required to honor readonly on unsupported input types. Even if a browser appears to respect it today, that behavior is not guaranteed and could change.
Form submission behavior: There’s a critical difference between readonly and disabled. A readonly field’s value is included in form submission data, while a disabled field’s value is not. If you swap readonly for disabled to fix this error, be aware that the field’s value won’t be sent with the form unless you take additional steps (such as adding a hidden input).
Accessibility: Screen readers and assistive technologies rely on valid HTML to convey the correct state of form controls. Using readonly on an unsupported type can send confusing signals about the control’s interactivity.
How to fix it
You have several options depending on your goal:
- Remove readonly if it was added by mistake or isn’t necessary.
- Change the input type to one that supports readonly, if that fits your use case.
- Use disabled instead to prevent interaction on non-textual inputs. Remember that disabled fields are excluded from form submission.
- Pair disabled with a hidden input if you need the value submitted with the form but want the visible control to be non-interactive.
Examples
Invalid: readonly on a checkbox
<input type="checkbox" name="agree" readonly>
Invalid: readonly on a range input
<input type="range" name="volume" min="0" max="100" value="50" readonly>
Invalid: readonly on a color input
<input type="color" name="theme" value="#ff0000" readonly>
Fixed: using readonly on a supported input type
<input type="text" name="code" value="ABC-123" readonly>
<input type="email" name="contact" value="user@example.com" readonly>
<input type="date" name="start" value="2024-01-15" readonly>
Fixed: using disabled for a non-textual input
<input type="checkbox" name="agree" checked disabled>
Fixed: using disabled with a hidden input to preserve form submission
If you need the value to be submitted with the form while keeping the visible control non-interactive, pair a disabled control with a hidden input:
<!-- The hidden input ensures the value is submitted -->
<input type="hidden" name="agree" value="on">
<!-- The disabled checkbox is visible but non-interactive -->
<input type="checkbox" name="agree_display" checked disabled>
<label for="agree_display">I agree to the terms</label>
Fixed: using JavaScript to prevent changes (advanced)
If you truly need a checkbox that looks interactive but can’t be changed, you can use JavaScript while keeping the HTML valid:
<input type="checkbox" name="agree" checked onclick="return false;">
Note that this approach relies on JavaScript and won’t work if scripting is disabled. For most cases, using disabled (with or without a companion hidden input) is the simpler and more robust solution.
The required attribute is a boolean attribute that tells the browser a field must be filled in before the form can be submitted. However, not every input type supports this concept. Some input types always have a value (like range, which defaults to a midpoint, or color, which defaults to #000000), while others represent actions rather than user data (like submit, reset, image, and button). For hidden inputs, the user has no way to interact with the field at all, so requiring them to provide a value makes no sense.
The HTML specification explicitly limits required to the following input types: checkbox, date, datetime-local, email, file, month, number, password, radio, search, tel, text, time, url, and week.
Using required on an unsupported type is invalid HTML. Browsers will typically ignore the attribute in this situation, which means you might believe a field is required when it actually isn’t being validated at all. This can lead to forms being submitted with missing or unexpected data. It also creates confusion for assistive technologies — screen readers may announce a field as required even though the browser won’t enforce it, misleading users.
How to fix it
- Check the input type. If you’re using required on an input with a type like hidden, range, color, submit, reset, image, or button, the attribute is not allowed.
- Remove the required attribute if the input type inherently provides a value or doesn’t accept user-provided data.
- Change the input type if you actually need the field to be required and the current type doesn’t match your intent.
- Use server-side validation for inputs like hidden that can’t use required but still need a value.
Examples
❌ Invalid: required on a hidden input
<form>
<input type="hidden" name="token" required>
<button type="submit">Submit</button>
</form>
The user cannot interact with a hidden input, so required is not allowed here. The browser won’t enforce it.
❌ Invalid: required on a range input
<form>
<label for="volume">Volume:</label>
<input type="range" id="volume" name="volume" min="0" max="100" required>
<button type="submit">Submit</button>
</form>
A range input always has a value (it defaults to the midpoint), so required is meaningless and not permitted.
❌ Invalid: required on a color input
<form>
<label for="color">Pick a color:</label>
<input type="color" id="color" name="color" required>
<button type="submit">Submit</button>
</form>
A color input always has a value (defaulting to #000000), so required is not valid here.
✅ Valid: required removed from unsupported types
<form>
<input type="hidden" name="token" value="abc123">
<label for="volume">Volume:</label>
<input type="range" id="volume" name="volume" min="0" max="100">
<button type="submit">Submit</button>
</form>
✅ Valid: required on supported input types
<form>
<label for="email">Email:</label>
<input type="email" id="email" name="email" required>
<label for="dob">Date of birth:</label>
<input type="date" id="dob" name="dob" required>
<label>
<input type="checkbox" name="terms" required>
I agree to the terms
</label>
<button type="submit">Submit</button>
</form>
These input types — email, date, and checkbox — all accept direct user input and are on the allowed list for the required attribute.
The seamless attribute was originally drafted as part of the HTML5 specification to allow an <iframe> to appear as though its content were part of the containing document. When present, it was supposed to remove borders, inherit styles from the parent page, and allow the iframe content to participate in the parent document’s styling context. However, no browser ever fully implemented the attribute, and it was officially removed from the WHATWG HTML Living Standard.
Because seamless is not a recognized attribute in the current HTML specification, using it triggers a validation error. Beyond validation, including it has no practical effect in any modern browser — it’s simply ignored. Keeping unsupported attributes in your markup creates confusion for other developers who may assume the attribute is doing something meaningful. It also adds unnecessary clutter to your HTML.
How to Fix It
Remove the seamless attribute from any <iframe> elements. If you want to replicate the visual effects that seamless was intended to provide, you can use CSS:
- Remove the border: Apply border: none; or use the frameborder="0" attribute (though frameborder is also obsolete — CSS is preferred).
- Blend the background: Set background: transparent; and add the allowtransparency attribute if targeting older browsers.
- Auto-resize to content: Use JavaScript to dynamically adjust the iframe’s height based on its content (subject to same-origin restrictions).
Note that true seamless integration — where the iframe inherits parent styles and its content flows naturally within the parent document — is not achievable with standard iframe behavior. If you need that level of integration, consider including the content directly in the page, using a server-side include, or fetching the content with JavaScript via the fetch API and inserting it into the DOM.
Examples
❌ Invalid: Using the seamless attribute
<iframe src="widget.html" seamless></iframe>
✅ Fixed: Attribute removed, CSS used for styling
<iframe src="widget.html" style="border: none;"></iframe>
✅ Fixed: Using a CSS class for cleaner markup
<style>
.seamless-iframe {
border: none;
width: 100%;
background: transparent;
}
</style>
<iframe src="widget.html" class="seamless-iframe" title="Embedded widget"></iframe>
✅ Alternative: Embedding content directly instead of using an iframe
If the content is on the same origin and you need true seamless integration, consider loading it directly:
<div id="embedded-content">
<!-- Content loaded via server-side include or JavaScript fetch -->
</div>
HTML has a defined set of global attributes (such as id, class, lang, and title) and element-specific attributes. Any attribute that isn’t part of these recognized sets will trigger a validation error. The st_title attribute is a proprietary, non-standard attribute that was used by older versions of the ShareThis sharing widget to pass metadata — specifically, the title of the content being shared.
The HTML5 specification introduced data-* attributes as the standard mechanism for embedding custom data on elements. These attributes allow developers to store arbitrary information without conflicting with the HTML spec. Newer versions of ShareThis and similar services have adopted this convention, but legacy code — especially in CMS themes, plugins, or modules — may still use the old non-standard format.
Using invalid attributes causes several problems:
- Standards compliance: The document fails W3C validation, which can indicate deeper markup quality issues.
- Future compatibility: Browsers are not required to handle non-standard attributes in any predictable way. Future browser updates could ignore or strip them.
- Maintainability: Non-standard attributes make code harder for other developers to understand and maintain.
- Accessibility tools: Screen readers and other assistive technologies rely on well-formed HTML. Invalid attributes can cause unexpected behavior in these tools.
To fix this, replace all proprietary ShareThis attributes with their data-* equivalents. For example, st_title becomes data-st-title, st_url becomes data-st-url, and displayText becomes data-st-displaytext. You should also update the ShareThis JavaScript library to a version that recognizes the new attribute format.
Examples
❌ Invalid: Using proprietary attributes
<span class="st_sharethis" st_title="My Article" st_url="https://example.com/article" displayText="ShareThis">
Share
</span>
This triggers validation errors for st_title, st_url, and displayText because none of these are valid HTML attributes.
✅ Valid: Using data-* attributes
<span class="st_sharethis" data-st-title="My Article" data-st-url="https://example.com/article" data-st-displaytext="ShareThis">
Share
</span>
All custom data is now stored in properly namespaced data-* attributes, which are fully compliant with the HTML5 specification.
✅ Valid: Using a button element with data-* attributes
If the element is interactive (e.g., it triggers a share action on click), consider using a <button> instead of a <span> for better accessibility:
<button type="button" class="st_sharethis" data-st-title="My Article" data-st-url="https://example.com/article">
Share this article
</button>
Fixing this in a CMS
If you’re using Drupal, WordPress, or another CMS with a ShareThis module or plugin:
- Update the plugin/module to the latest version — most have already migrated to data-* attributes.
- Check your theme templates for hardcoded ShareThis markup that may still use the old attribute format.
- Search your codebase for st_title, st_url, and displayText and replace them with data-st-title, data-st-url, and data-st-displaytext respectively.
- Update the ShareThis JavaScript to a version compatible with the new attribute names, and verify that sharing functionality still works after the change.
HTML has a defined set of global attributes (like class, id, title, lang, etc.) and element-specific attributes that are permitted by the specification. Any attribute that falls outside this set — such as st_url, st_title, or displayText — will trigger a validation error because the browser and the validator don’t recognize it as part of the HTML standard.
The st_url attribute, along with related attributes like st_title, st_via, and displayText, originated from early versions of the ShareThis social sharing library. These were proprietary attributes that the ShareThis JavaScript would read from the DOM to configure sharing behavior. While browsers generally ignore attributes they don’t understand (so the page may still appear to work), using non-standard attributes violates the HTML specification and can cause several problems:
- Standards compliance: Invalid HTML can lead to unpredictable behavior across different browsers and future browser versions.
- Accessibility: Screen readers and other assistive technologies may not handle non-standard attributes correctly, potentially causing confusion.
- Maintainability: Non-standard markup is harder for other developers to understand and maintain.
- SEO: Search engine crawlers may penalize or misinterpret pages with significant validation errors.
HTML5 introduced data-* attributes specifically to solve this problem. Any custom data you need to attach to an element can use this pattern — for example, data-st-url instead of st_url. The ShareThis library itself updated its integration to use data-* attributes in later versions, so modern implementations should already follow this pattern.
How to Fix
- Replace proprietary attributes with data-* equivalents: Convert st_url to data-st-url, st_title to data-st-title, displayText to data-display-text, and so on.
- Update your ShareThis integration: If you’re using an outdated version of ShareThis (or an outdated CMS module/plugin like an old Drupal integration), update to the latest version, which uses valid HTML5 attributes.
- Check your CMS templates: If the invalid attributes are hardcoded in a theme template or a content block, update them manually.
Examples
Invalid: Proprietary attributes on a <span>
<span class="st_facebook_large" displayText="Facebook" st_url="https://example.com" st_title="My Page Title"></span>
<span class="st_twitter_large" displayText="Twitter" st_url="https://example.com" st_title="My Page Title"></span>
This markup uses st_url, st_title, and displayText, none of which are valid HTML attributes.
Valid: Using data-* attributes instead
<span class="st_facebook_large" data-display-text="Facebook" data-st-url="https://example.com" data-st-title="My Page Title"></span>
<span class="st_twitter_large" data-display-text="Twitter" data-st-url="https://example.com" data-st-title="My Page Title"></span>
By prefixing each custom attribute with data-, the markup becomes valid HTML5. Note that you may also need to update the associated JavaScript to read from these new attribute names (e.g., using element.dataset.stUrl instead of element.getAttribute('st_url')).
Valid: Modern ShareThis integration
If you’re updating your ShareThis integration entirely, the modern approach uses a different markup pattern:
<div class="sharethis-inline-share-buttons" data-url="https://example.com" data-title="My Page Title"></div>
Modern versions of the ShareThis library expect data-* attributes by default, so upgrading the library and its associated markup is the cleanest solution. Check the ShareThis documentation for the latest recommended integration approach for your platform.
The type attribute is specific to the <input> element, where it determines the kind of input control rendered — such as text, checkbox, email, or number. The <textarea> element serves a single, distinct purpose: providing a multi-line plain-text editing area. Because it doesn’t have multiple modes of operation, the HTML specification does not define a type attribute for it.
This error commonly occurs when developers confuse <textarea> with <input>, or when refactoring a single-line <input type="text"> into a multi-line <textarea> without removing the now-invalid type attribute. It can also appear when using templating systems or frameworks that apply attributes generically across different form elements.
While browsers will typically ignore the unrecognized type attribute on a <textarea>, keeping it in your markup causes W3C validation errors and can lead to confusion for other developers reading the code. Invalid attributes may also interfere with CSS attribute selectors (e.g., textarea[type="text"] is a selector targeting invalid markup), accessibility tools, or automated testing systems that rely on well-formed HTML.
To fix this issue, remove the type attribute from the <textarea> element. If you need to differentiate between multiple textareas, use id, name, or class attributes instead. If you actually need a single-line text input, use <input type="text"> rather than <textarea>.
Examples
Incorrect: type attribute on <textarea>
<label for="comment">Your comment:</label>
<textarea type="text" id="comment" name="comment" rows="4" cols="50"></textarea>
The type="text" attribute is not valid on <textarea> and will trigger a validation error.
Correct: <textarea> without type
<label for="comment">Your comment:</label>
<textarea id="comment" name="comment" rows="4" cols="50"></textarea>
Simply removing the type attribute resolves the issue. The <textarea> element inherently provides a multi-line text input, so no type is needed.
Correct: Using <input> when a single-line field is intended
If you intended a single-line text field, use <input> instead:
<label for="username">Username:</label>
<input type="text" id="username" name="username">
The type attribute is valid and expected on <input> elements, where it controls the type of input control rendered.
HTML forms have built-in constraint validation that is enabled by default. When a user submits a form, the browser automatically checks inputs against their constraints (such as required, type="email", pattern, min, max, etc.) and prevents submission if any validation fails. There is no need to add a validate attribute to opt into this behavior because it is the default.
The HTML specification defines novalidate as a valid boolean attribute on the <form> element to disable this default validation, but it does not define a corresponding validate attribute. Using validate will trigger a W3C validation error because the browser doesn’t recognize it and will simply ignore it.
This matters for several reasons:
- Standards compliance: Invalid attributes make your HTML non-conforming, which can cause issues with automated testing and quality tools.
- Developer confusion: A validate attribute suggests it’s doing something functional, but it has no effect. Future maintainers may incorrectly believe it’s enabling validation and be reluctant to remove it.
- Accessibility and tooling: Screen readers and other assistive technologies rely on well-formed HTML. Unrecognized attributes can lead to unpredictable behavior in some user agents.
To fix this issue, determine your intent:
- If you want form validation enabled — simply remove the validate attribute. Validation is on by default.
- If you want form validation disabled — replace validate with novalidate.
Examples
Incorrect: using the invalid validate attribute
<form validate action="/submit">
<label for="email">Email:</label>
<input type="email" id="email" name="email" required>
<button type="submit">Submit</button>
</form>
This triggers the error: Attribute “validate” not allowed on element “form” at this point.
Correct: relying on default validation (attribute removed)
Since constraint validation is enabled by default, simply remove the invalid attribute:
<form action="/submit">
<label for="email">Email:</label>
<input type="email" id="email" name="email" required>
<button type="submit">Submit</button>
</form>
The browser will automatically validate the email input — checking both the required constraint and that the value matches a valid email format — before allowing submission.
Correct: using novalidate to disable validation
If your intention is to disable built-in validation (for example, because you handle validation with JavaScript), use the novalidate attribute instead:
<form novalidate action="/submit">
<label for="city">City:</label>
<input type="text" id="city" name="city" required>
<button type="submit">Submit</button>
</form>
In this example, even though the input has the required attribute, the browser will not prevent form submission when the field is empty, because novalidate tells the browser to skip constraint validation on submission.
Using formnovalidate on a specific button
If you want validation enabled for normal submission but want to bypass it for a specific button (such as a “Save Draft” button), use the formnovalidate attribute on that button instead of disabling validation for the entire form:
<form action="/submit">
<label for="name">Name:</label>
<input type="text" id="name" name="name" required>
<button type="submit">Submit</button>
<button type="submit" formnovalidate formaction="/save-draft">Save Draft</button>
</form>
The “Submit” button will enforce validation, while the “Save Draft” button will skip it. This approach gives you fine-grained control without needing an invalid attribute.
When you write an attribute with an equals sign, the HTML specification expects a value to follow — either quoted ("value") or unquoted. An attribute like style= or class= leaves the parser in an ambiguous state because the equals sign signals that a value is coming, but nothing is there. While browsers may attempt to recover from this error, their behavior is not guaranteed to be consistent, and the resulting interpretation may not match your intent.
This matters for several reasons. First, it produces invalid HTML that fails W3C validation, which can be a requirement for accessibility compliance and professional standards. Second, certain attributes like style, class, or id with missing values can cause unexpected behavior in CSS selectors or JavaScript DOM queries. Third, assistive technologies such as screen readers may misinterpret malformed attributes, potentially degrading the experience for users with disabilities.
There are a few ways to fix this issue depending on what you intended:
- Provide a value: Add the missing value in quotes after the equals sign (e.g., class="container").
- Use an empty string: If you intentionally want a blank value, explicitly use empty quotes (e.g., style="").
- Remove the attribute: If the attribute isn’t needed, delete it entirely.
- Remove the equals sign: If the attribute is a boolean attribute like disabled or hidden, it doesn’t need an equals sign or a value at all.
Examples
Missing attribute value (invalid)
<!-- The style attribute has an equals sign but no value -->
<p style=>This paragraph has an invalid attribute.</p>
<!-- The class attribute is missing its value -->
<div class=>Content here</div>
<!-- The id attribute has no value after the equals sign -->
<span id=>Some text</span>
Providing a proper value (valid)
<!-- Correct: the style attribute has a value -->
<p style="color: blue;">This paragraph is styled correctly.</p>
<!-- Correct: the class attribute has a value -->
<div class="wrapper">Content here</div>
Using an empty string explicitly (valid)
If you genuinely need the attribute present but with no meaningful value, use empty quotes. This is syntactically valid, though often unnecessary:
<p style="">This paragraph has an explicitly empty style.</p>
Boolean attributes without equals signs (valid)
Boolean attributes like disabled, hidden, and readonly don’t require a value. If you accidentally wrote one of these with a trailing equals sign, simply remove it:
<!-- Invalid: equals sign with no value -->
<button disabled=>Submit</button>
<!-- Valid: boolean attribute with no equals sign -->
<button disabled>Submit</button>
Common mistake with dynamic templates
This error frequently appears when a templating engine or framework outputs an attribute with a missing or null value. For example, server-rendered HTML might produce:
<!-- A template rendered an empty value -->
<a href=>Click here</a>
The fix is to ensure your template logic provides a valid value or omits the attribute entirely:
<a href="https://example.com">Click here</a>
In XML-based formats like XHTML 1.0 or other XML vocabularies, namespace declarations such as xmlns:dt="uuid:C2F41010-65B3-11d1-A29F-00AA00C14882" were used to associate element or attribute prefixes with specific namespace URIs. This allowed different XML vocabularies to coexist in a single document without naming conflicts. The xmlns:dt namespace in particular was commonly associated with Microsoft’s XML data types, often seen in legacy ASP or older Microsoft-generated HTML.
HTML5, however, does not use the XML namespace mechanism. The HTML parser treats the document as HTML, not XML, and does not recognize or process custom namespace prefixes. The only xmlns-related attributes permitted in HTML5 are:
- xmlns on the <html> element, but only with the value http://www.w3.org/1999/xhtml (for compatibility with XHTML-serving scenarios).
- Implicit namespace handling for embedded <svg> and <math> elements, which the HTML parser manages automatically.
Any other xmlns:* attribute — such as xmlns:dt, xmlns:o, xmlns:v, or xmlns:st1 — is invalid in HTML5 and will trigger the W3C validator error: “Attribute with the local name ‘xmlns:dt’ is not serializable as XML 1.0.”
Why this is a problem
- Standards compliance: Custom XML namespace attributes violate the HTML5 specification. The HTML parser does not process them, so they serve no functional purpose.
- Serialization issues: If the document is ever round-tripped through an XML serializer (for example, when converting HTML to XHTML), these attributes cannot be properly serialized under XML 1.0 rules, potentially causing parsing failures.
- Legacy baggage: These attributes typically appear in documents generated by older tools (such as Microsoft Word’s “Save as HTML” feature) and carry over data-type or Office-specific namespace declarations that are meaningless in a modern web context.
- Document bloat: Keeping unused namespace declarations adds unnecessary bytes to your document without any benefit.
How to fix it
- Search your HTML for any attributes starting with xmlns: on the <html> element or elsewhere in the document.
- Remove them entirely. If your code relied on XML data types or Office-specific features tied to these namespaces, you’ll need to refactor that logic using standard HTML5 attributes (such as data-* attributes) or JavaScript.
- Ensure your document uses a standard HTML5 <!DOCTYPE html> declaration and a clean <html> tag.
If you’re working with content pasted from Microsoft Word or similar tools, consider running it through an HTML cleaner to strip out all Office-specific markup.
Examples
Incorrect — custom xmlns:dt attribute
This triggers the validation error because xmlns:dt is not a valid attribute in HTML5:
<html xmlns:dt="uuid:C2F41010-65B3-11d1-A29F-00AA00C14882">
<head>
<title>My Page</title>
</head>
<body>
<p>Hello world</p>
</body>
</html>
Incorrect — multiple custom namespace declarations
Documents exported from older tools often include several invalid namespace attributes at once:
<html xmlns:o="urn:schemas-microsoft-com:office:office"
xmlns:dt="uuid:C2F41010-65B3-11d1-A29F-00AA00C14882"
xmlns:v="urn:schemas-microsoft-com:vml">
<head>
<title>My Page</title>
</head>
<body>
<p>Hello world</p>
</body>
</html>
Correct — clean HTML5 document
Remove all custom xmlns:* attributes and use a standard HTML5 structure:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>My Page</title>
</head>
<body>
<p>Hello world</p>
</body>
</html>
Correct — using data attributes as a replacement
If you previously relied on namespace-prefixed attributes to store custom data on elements, use data-* attributes instead:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>My Page</title>
</head>
<body>
<p data-type="datetime">2024-01-15</p>
</body>
</html>
Understanding the Issue
In the early days of the Open Graph Protocol (used by Facebook, LinkedIn, and other platforms to parse shared links), developers were instructed to add XML namespace declarations like xmlns:og and xmlns:fb to the <html> element. This was borrowed from XHTML conventions where custom prefixed attributes required namespace bindings.
However, modern HTML5 documents are not XML. The HTML5 parser does not support arbitrary namespace declarations in the way XHTML/XML does. Attributes with colons in their local names (like xmlns:og) are not serializable as XML 1.0, meaning they cannot be reliably round-tripped between HTML and XML parsers. The W3C validator flags this because these attributes violate the HTML serialization rules.
The good news is that these namespace declarations have never been required for Open Graph tags to work. Social media crawlers (Facebook’s, Twitter’s, LinkedIn’s, etc.) parse <meta> tags based on the property attribute value directly — they don’t rely on XML namespace resolution. Browsers also ignore these attributes entirely, so removing them has zero impact on functionality.
How to Fix It
- Locate your <html> opening tag.
- Remove any xmlns:og, xmlns:fb, or similar namespace attributes.
- Keep your Open Graph <meta> tags exactly as they are — they will continue to work.
Examples
❌ Incorrect: Namespace attributes on the <html> element
<!DOCTYPE html>
<html lang="en" xmlns:og="http://ogp.me/ns#" xmlns:fb="http://www.facebook.com/2008/fbml">
<head>
<title>My Page</title>
<meta property="og:title" content="My Page Title">
<meta property="og:description" content="A description of my page.">
<meta property="og:image" content="https://example.com/image.jpg">
</head>
<body>
<p>Hello, world!</p>
</body>
</html>
This triggers the validator warning: Attribute with the local name “xmlns:og” is not serializable as XML 1.0 (and similarly for xmlns:fb).
✅ Correct: Clean <html> element without namespace attributes
<!DOCTYPE html>
<html lang="en">
<head>
<title>My Page</title>
<meta property="og:title" content="My Page Title">
<meta property="og:description" content="A description of my page.">
<meta property="og:image" content="https://example.com/image.jpg">
</head>
<body>
<p>Hello, world!</p>
</body>
</html>
The Open Graph <meta> tags remain unchanged and will continue to be recognized by Facebook, LinkedIn, and all other platforms that support the Open Graph Protocol. The only change is removing the unnecessary xmlns:og and xmlns:fb attributes from the <html> tag.
Common Variations
You may also encounter other namespace prefixes like xmlns:article, xmlns:product, or xmlns:music. These are all unnecessary in HTML5 and should be removed:
<!-- ❌ Remove all of these -->
<html lang="en" xmlns:og="http://ogp.me/ns#" xmlns:article="http://ogp.me/ns/article#" xmlns:fb="http://www.facebook.com/2008/fbml">
<!-- ✅ Keep it simple -->
<html lang="en">
Note that the standard xmlns attribute (without a colon prefix) is allowed on the <html> element in HTML5 if its value is exactly http://www.w3.org/1999/xhtml. However, even this is unnecessary and can be omitted. The prefixed forms like xmlns:og are never valid in HTML5.
The W3C HTML Validator raises this error when it encounters a namespaced attribute such as xmlns:m, xmlns:o, xmlns:v, or any other xmlns:* declaration in an HTML document. These prefixed namespace bindings are an XML concept defined in the XML Namespaces specification. In the HTML syntax (documents served with the text/html MIME type), the HTML parser does not treat these as namespace declarations — it treats them as regular attributes with a colon in the name. Because such attribute names are not serializable in XML 1.0, the validator reports the error.
This issue is extremely common with content exported from Microsoft Office (Word, Excel, PowerPoint). When you save or copy Office content as HTML, the generated markup often includes namespace declarations like xmlns:m (Office Math Markup Language), xmlns:o (Office namespace), xmlns:v (VML), and xmlns:w (Word-specific markup). These declarations were designed for older, XML-based HTML rendering and serve no purpose in modern HTML5 documents.
Why This Is a Problem
- Standards compliance: The HTML5 specification only permits the xmlns attribute (without a prefix) on the <html> element, and only with the value http://www.w3.org/1999/xhtml. Prefixed forms like xmlns:m are not allowed.
- Serialization: If a tool attempts to serialize the DOM as XML (for example, XMLSerializer), attributes with colons that aren’t properly bound namespaces can cause failures or unexpected output.
- No functional benefit: In an HTML document, the browser’s HTML parser ignores these namespace bindings. They don’t enable any special behavior — they’re dead weight in your markup.
- Maintainability: Leaving Office-generated namespace clutter in your HTML makes the code harder to read and maintain.
How to Fix It
- Remove the xmlns:* attribute from your HTML element (or whichever element it appears on). In most cases, this is all you need to do — the namespaced content from Office isn’t rendered by browsers anyway.
- Clean up Office-generated HTML by stripping out all proprietary namespaces, conditional comments, and Office-specific elements. Tools like HTML Tidy or your editor’s “paste as plain text” feature can help.
- Use native HTML5 equivalents where possible. HTML5 natively supports MathML and SVG without requiring explicit namespace declarations.
- Switch to XHTML only if you have a genuine need for XML namespaces. This means serving the document with the application/xhtml+xml MIME type and using well-formed XML syntax throughout.
Examples
Incorrect: Office-generated namespace declarations
This markup, typical of content exported from Microsoft Word, triggers the validation error:
<!DOCTYPE html>
<html xmlns:m="http://schemas.microsoft.com/office/2004/12/omml"
xmlns:o="urn:schemas-microsoft-com:office:office"
xmlns:v="urn:schemas-microsoft-com:vml">
<head>
<title>Office Export</title>
</head>
<body>
<p>Content from Word</p>
</body>
</html>
Correct: Clean HTML5 without namespace declarations
Remove all xmlns:* attributes and any associated Office-specific markup:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Office Export</title>
</head>
<body>
<p>Content from Word</p>
</body>
</html>
Incorrect: Single namespace on a non-html element
The error can also appear on other elements if a tool inserts namespace attributes:
<div xmlns:custom="http://example.com/ns">
<p>Some content</p>
</div>
Correct: Remove the namespace attribute
<div>
<p>Some content</p>
</div>
Using MathML natively in HTML5
If the xmlns:m attribute was added to support math content, note that HTML5 supports MathML directly without any namespace declaration:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>MathML in HTML5</title>
</head>
<body>
<p>The quadratic formula:</p>
<math>
<mi>x</mi>
<mo>=</mo>
<mfrac>
<mrow>
<mo>-</mo>
<mi>b</mi>
<mo>±</mo>
<msqrt>
<msup><mi>b</mi><mn>2</mn></msup>
<mo>-</mo>
<mn>4</mn><mi>a</mi><mi>c</mi>
</msqrt>
</mrow>
<mrow>
<mn>2</mn><mi>a</mi>
</mrow>
</mfrac>
</math>
</body>
</html>
No xmlns:m attribute is needed — the browser recognizes <math> as MathML automatically.
The W3C HTML Validator raises this error because HTML5 does not support arbitrary XML namespace declarations. In XML, the xmlns: prefix is used to bind namespace prefixes to URIs, allowing elements and attributes from different vocabularies to coexist. However, HTML5 uses its own parsing rules that are distinct from XML, and the only namespace attribute recognized in HTML5 is the plain xmlns attribute on the <html> element (set to http://www.w3.org/1999/xhtml). Colonized namespace attributes like xmlns:o, xmlns:v, xmlns:w, and others are not part of the HTML5 specification.
The xmlns:o="urn:schemas-microsoft-com:office:office" namespace specifically comes from Microsoft Office. When you save a Word document as HTML or copy-paste content from Office applications into an HTML editor, Office injects its own namespace declarations and proprietary markup. This markup is intended for round-tripping the document back into Office and serves no purpose on the web.
Beyond validation, leaving these attributes in place can cause practical problems. The HTML5 parser in browsers silently ignores or misinterprets these namespace declarations, meaning they add dead weight to your markup. They also increase file size unnecessarily and can confuse other tools that process your HTML, such as screen readers, search engine crawlers, or content management systems.
How to Fix
- Remove the xmlns:o attribute from any element where it appears (typically the <html> tag).
- Remove related Office namespace attributes such as xmlns:v, xmlns:w, xmlns:x, and xmlns:st1, as these will trigger similar errors.
- Remove any elements or attributes using those namespace prefixes, such as <o:p>, <v:shape>, or <w:wrap>, since they are not valid HTML5 elements and browsers do not render them meaningfully.
- Clean up Office-generated HTML thoroughly if you’re converting Word documents to web content. Consider using a dedicated HTML cleaning tool or a paste-as-plain-text option in your editor.
Examples
Incorrect: Office namespace attributes on the <html> element
This markup contains multiple Microsoft Office namespace declarations that trigger validation errors:
<!DOCTYPE html>
<html xmlns:o="urn:schemas-microsoft-com:office:office"
xmlns:w="urn:schemas-microsoft-com:office:word"
xmlns:v="urn:schemas-microsoft-com:vml">
<head>
<title>My Document</title>
</head>
<body>
<h1>Meeting Notes</h1>
<p>Welcome to the meeting.<o:p></o:p></p>
</body>
</html>
Correct: Clean HTML5 without Office namespaces
Remove all xmlns: prefixed attributes and any Office-specific elements like <o:p>:
<!DOCTYPE html>
<html lang="en">
<head>
<title>My Document</title>
</head>
<body>
<h1>Meeting Notes</h1>
<p>Welcome to the meeting.</p>
</body>
</html>
Incorrect: Office namespace on a non-root element
Sometimes Office markup appears deeper in the document:
<div xmlns:o="urn:schemas-microsoft-com:office:office">
<p>Some content<o:p></o:p></p>
</div>
Correct: Cleaned-up version
<div>
<p>Some content</p>
</div>
By stripping out all Microsoft Office namespace declarations and their associated proprietary elements, your HTML becomes standards-compliant, lighter, and more compatible across browsers and assistive technologies. If you frequently convert Office documents to HTML, consider using a cleanup tool like DirtyMarkup or the “Paste as plain text” feature in your content editor to avoid these issues from the start.
When graphic design tools like Affinity Designer (formerly Serif) export SVG files, they often embed custom namespace declarations such as xmlns:serif="http://www.serif.com/". These namespaces allow the editor to store its own metadata — like layer names, grouping information, or application-specific settings — inside the SVG file. While this metadata is useful if you re-open the file in the original editor, it has no meaning in a web browser.
The HTML5 specification defines a specific set of namespace attributes that are allowed in SVG elements (such as xmlns, xmlns:xlink, and xmlns:xml). Any namespace prefix not in this predefined list — like xmlns:serif, xmlns:inkscape, or xmlns:sodipodi — triggers this validation error because the HTML parser cannot serialize these attributes back into well-formed XML 1.0. This isn’t just a theoretical concern: non-serializable attributes can cause issues when the DOM is manipulated via JavaScript or when the markup is processed by XML-based tools.
Beyond the xmlns:serif declaration itself, you’ll likely find attributes in the SVG that use this namespace prefix, such as serif:id="layer1". These should also be removed since they reference a namespace the browser doesn’t understand.
How to Fix It
- Remove the xmlns:serif attribute from the <svg> element.
- Remove any attributes prefixed with serif: (e.g., serif:id) from child elements within the SVG.
- If you re-export the SVG, check your editor’s export settings — some tools offer a “clean” or “optimized” export option that strips proprietary metadata.
- Consider using an SVG optimization tool like SVGO to automatically clean up unnecessary attributes.
Examples
❌ Invalid: SVG with xmlns:serif attribute
<svg xmlns="http://www.w3.org/2000/svg"
xmlns:serif="http://www.serif.com/"
viewBox="0 0 100 100"
width="100"
height="100">
<g serif:id="Layer 1">
<circle cx="50" cy="50" r="40" fill="blue" />
</g>
</svg>
This triggers the error because xmlns:serif is not a recognized namespace in HTML5, and serif:id references that unsupported namespace.
✅ Valid: SVG with proprietary attributes removed
<svg xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 100 100"
width="100"
height="100">
<g>
<circle cx="50" cy="50" r="40" fill="blue" />
</g>
</svg>
Both xmlns:serif and serif:id have been removed. The SVG renders identically in the browser since those attributes were only meaningful to the editing application.
Handling Multiple Proprietary Namespaces
Exported SVGs sometimes contain several non-standard namespaces at once. Remove all of them:
<!-- ❌ Invalid: multiple proprietary namespaces -->
<svg xmlns="http://www.w3.org/2000/svg"
xmlns:serif="http://www.serif.com/"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
viewBox="0 0 200 200">
<rect x="10" y="10" width="180" height="180" fill="red"
serif:id="background"
inkscape:label="bg-rect" />
</svg>
<!-- ✅ Valid: all proprietary namespaces and prefixed attributes removed -->
<svg xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 200 200">
<rect x="10" y="10" width="180" height="180" fill="red" />
</svg>
If you frequently work with SVGs from design tools, integrating an SVG optimizer into your build process can save time and ensure these non-standard attributes never reach production.
The xmlns attribute defines the XML namespace for an element. For SVG, the correct namespace is http://www.w3.org/2000/svg, declared with xmlns="http://www.w3.org/2000/svg". The xmlns:svg attribute attempts to declare an additional prefixed namespace binding — essentially mapping the prefix svg: to the same namespace URI. This is redundant because the default (unprefixed) namespace already covers all SVG elements.
In HTML5, the parser handles namespaces internally. The HTML specification only permits a small set of namespace attributes: xmlns on certain elements (like <svg> and <math>) and xmlns:xlink for legacy compatibility. Arbitrary prefixed namespace declarations like xmlns:svg are not part of the HTML serialization format. The W3C validator raises this error because attributes containing colons in their local names (other than the specifically allowed ones) cannot be round-tripped through the HTML parser and serializer — they are “not serializable as XML 1.0.”
Why this matters
- Standards compliance: HTML5 has strict rules about which namespace declarations are permitted. Using xmlns:svg violates these rules.
- Serialization issues: If a browser parses the HTML and then re-serializes it (e.g., via innerHTML), the xmlns:svg attribute may be lost, altered, or cause unexpected behavior because it falls outside the serializable attribute set.
- Redundancy: Even in pure XML/SVG documents, declaring xmlns:svg="http://www.w3.org/2000/svg" alongside xmlns="http://www.w3.org/2000/svg" is unnecessary. The default namespace already applies to the <svg> element and all its unprefixed descendants.
How to fix it
- Locate the <svg> element (or any element) that contains the xmlns:svg attribute.
- Remove xmlns:svg="http://www.w3.org/2000/svg" entirely.
- Ensure the standard xmlns="http://www.w3.org/2000/svg" attribute remains if needed (note that when embedding SVG inline in an HTML5 document, even xmlns is optional since the HTML parser infers the namespace automatically).
Examples
Incorrect: redundant prefixed namespace declaration
<svg xmlns="http://www.w3.org/2000/svg" xmlns:svg="http://www.w3.org/2000/svg" viewBox="0 0 100 100">
<circle cx="50" cy="50" r="40" fill="blue" />
</svg>
The xmlns:svg attribute triggers the validation error because it is not serializable in HTML.
Correct: standard namespace only
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100">
<circle cx="50" cy="50" r="40" fill="blue" />
</svg>
Correct: inline SVG in HTML5 without any namespace attribute
When SVG is embedded directly in an HTML5 document, the HTML parser automatically assigns the correct namespace, so you can omit xmlns altogether:
<svg viewBox="0 0 100 100">
<circle cx="50" cy="50" r="40" fill="blue" />
</svg>
This is perfectly valid and is the most common pattern for inline SVG in modern HTML. The xmlns attribute is only strictly necessary when the SVG is served as a standalone XML file (with a .svg extension or an image/svg+xml content type).
The xmlns:v attribute is a namespace declaration that binds the v prefix to Microsoft’s VML namespace (urn:schemas-microsoft-com:vml). VML was a proprietary vector graphics format used primarily by Internet Explorer (versions 5 through 9) for rendering shapes, lines, and other graphical elements. When Microsoft dropped VML support in favor of SVG starting with IE 9, the technology became obsolete.
In HTML5 (the HTML living standard), namespace declarations using the xmlns: prefix pattern are not permitted. The HTML parser does not process these as actual namespace bindings — they are treated as regular attributes with a colon in the name. The validator flags this because such attributes cannot be round-tripped through an XML 1.0 serializer. An attribute name containing a colon implies a namespace prefix in XML, but without a proper namespace declaration in the XML output, the serialization would be invalid. This means your document cannot be reliably converted between HTML and XML formats.
This issue commonly appears in pages generated by older versions of Microsoft Office (Word, Outlook) that export to HTML, or in legacy templates that were designed for IE compatibility. You may also see similar warnings for related attributes like xmlns:o (Office namespace) or xmlns:w (Word namespace).
Why this matters
- Standards compliance: HTML5 explicitly does not support custom namespace declarations. Only the built-in namespaces for SVG and MathML are recognized.
- No functional benefit: Since no modern browser supports VML, the attribute serves no purpose. It adds dead weight to your markup.
- Interoperability: Documents with non-serializable attributes cannot be cleanly processed by XML-based tools, XSLT transformations, or any system that needs valid XML serialization.
How to fix it
- Remove the xmlns:v attribute from your <html> element (or wherever it appears).
- Remove any other legacy Microsoft namespace declarations such as xmlns:o, xmlns:w, or xmlns:x.
- Remove any VML-specific elements (like <v:shape>, <v:oval>, etc.) from your document, as they are not recognized by modern browsers.
- Replace VML graphics with SVG if you still need vector graphics functionality. SVG is natively supported in all modern browsers and is part of the HTML standard.
Examples
Incorrect: legacy VML namespace declaration
<!DOCTYPE html>
<html xmlns:v="urn:schemas-microsoft-com:vml" xmlns:o="urn:schemas-microsoft-com:office:office" lang="en">
<head>
<title>Legacy VML Page</title>
</head>
<body>
<v:oval style="width:100px;height:75px" fillcolor="blue"></v:oval>
</body>
</html>
This triggers the validator warning for both xmlns:v and xmlns:o, and the <v:oval> element is not recognized by any modern browser.
Correct: namespace removed, VML replaced with SVG
<!DOCTYPE html>
<html lang="en">
<head>
<title>Modern SVG Page</title>
</head>
<body>
<svg width="100" height="75" xmlns="http://www.w3.org/2000/svg">
<ellipse cx="50" cy="37.5" rx="50" ry="37.5" fill="blue" />
</svg>
</body>
</html>
Correct: simple removal when no vector graphics are needed
If the namespace was included unnecessarily (common with auto-generated HTML), simply remove it:
<!DOCTYPE html>
<html lang="en">
<head>
<title>Clean Page</title>
</head>
<body>
<p>No legacy namespace attributes needed.</p>
</body>
</html>
If your HTML was exported from Microsoft Office, consider running it through an HTML cleaner or manually stripping all xmlns:* attributes and proprietary elements. The resulting markup will be smaller, valid, and fully compatible with modern browsers.
HTML5 uses a specific serialization format that is distinct from XML. While XML allows you to declare arbitrary namespace prefixes using xmlns:prefix attributes, the HTML parser does not recognize or process these declarations. The HTML specification only supports a small set of predefined namespaces — the default HTML namespace, SVG (http://www.w3.org/2000/svg), MathML (http://www.w3.org/1998/Math/MathML), XLink, XML, and XMLNS — and these are handled implicitly through the appropriate embedding elements (<svg>, <math>), not through explicit xmlns: declarations.
The phrase “not serializable as XML 1.0” in the validator message means that this attribute cannot be round-tripped between the HTML parser and an XML serializer. The HTML parser treats xmlns:w as an opaque attribute name containing a colon, rather than as a namespace declaration. If you were to serialize this DOM back to XML, the result would be invalid because xmlns:w would be interpreted as a namespace declaration for a prefix that may conflict with actual element usage.
This issue almost always originates from content exported or pasted from Microsoft Word, Excel, or other Office applications. These tools generate markup laden with proprietary namespace declarations like xmlns:w, xmlns:o (Office), xmlns:v (VML), and xmlns:x (Excel). Along with these declarations come Office-specific elements and attributes (e.g., <w:WordDocument>, <o:OfficeDocumentSettings>) that have no meaning in a web browser and bloat your HTML.
Beyond being a validation error, leaving this markup in place creates several problems. It adds significant unnecessary weight to your pages, makes the source code harder to read and maintain, and signals that the HTML was auto-generated without cleanup — which can affect long-term maintainability. Browsers silently ignore these attributes and elements, so they provide no functional benefit.
How to fix it
- Remove all xmlns: prefixed attributes from your HTML elements, including xmlns:w, xmlns:o, xmlns:v, xmlns:x, and any others.
- Remove any Office-specific elements that use those namespace prefixes, such as <o:p>, <w:WordDocument>, or <v:shapetype>. These elements are meaningless in HTML5.
- Clean up associated conditional comments like <!--[if gte mso 9]> that often wrap Office-specific blocks.
- Consider using a paste-cleanup tool if you regularly paste content from Word into your HTML. Many CMS platforms and text editors offer “paste as plain text” or “clean HTML” options.
If you genuinely need to work with Office Open XML namespaces, use an appropriate XML-based format (XHTML served as application/xhtml+xml, or OOXML documents) rather than standard HTML5.
Examples
Incorrect — Office namespace declarations in HTML
This markup is typical of content saved or exported from Microsoft Word:
<html xmlns:w="urn:schemas-microsoft-com:office:word"
xmlns:o="urn:schemas-microsoft-com:office:office"
xmlns:v="urn:schemas-microsoft-com:vml">
<head>
<title>Report</title>
<!--[if gte mso 9]>
<w:WordDocument>
<w:View>Normal</w:View>
</w:WordDocument>
<![endif]-->
</head>
<body>
<p class="MsoNormal">Hello world<o:p></o:p></p>
</body>
</html>
Correct — Clean HTML without Office markup
<!DOCTYPE html>
<html lang="en">
<head>
<title>Report</title>
</head>
<body>
<p>Hello world</p>
</body>
</html>
Incorrect — Single namespace on the <html> element
Even a single custom namespace declaration triggers the error:
<html xmlns:w="urn:schemas-microsoft-com:office:word">
<head>
<title>My Page</title>
</head>
<body>
<p>Content here.</p>
</body>
</html>
Correct — Remove the attribute entirely
<!DOCTYPE html>
<html lang="en">
<head>
<title>My Page</title>
</head>
<body>
<p>Content here.</p>
</body>
</html>
Note that the default xmlns="http://www.w3.org/1999/xhtml" attribute on the <html> element is permitted (though unnecessary in HTML5), but any prefixed namespace declaration like xmlns:w is not.
When the W3C HTML Validator reports that an attribute is “not serializable as XML 1.0,” it means the attribute name contains characters that fall outside the allowed range defined by the XML 1.0 specification. HTML5 documents can be serialized as either HTML or XML (XHTML), and the validator checks that your markup is compatible with both serialization formats. Attribute names in XML 1.0 must start with a letter or underscore and can only contain letters, digits, hyphens, underscores, periods, and certain Unicode characters — they cannot include characters like {, }, @, $, or other symbols commonly found in templating languages.
This issue most frequently appears when a server-side or client-side templating engine (such as Mustache, Handlebars, Angular, Jinja2, or Blade) fails to fully process its expressions before the HTML reaches the browser. Instead of the template placeholder being replaced with a proper value, the raw template syntax ends up in the HTML as a malformed attribute. For example, {{class}} might appear as an attribute name rather than being resolved to its intended value.
Why this matters
- Standards compliance: HTML that isn’t serializable as XML 1.0 cannot be reliably converted to XHTML, which limits interoperability.
- Browser inconsistency: Browsers may handle invalid attribute names unpredictably, potentially ignoring the attribute entirely or misinterpreting surrounding markup.
- Accessibility: Malformed attributes can break ARIA attributes or other accessibility-related markup, making content inaccessible to assistive technologies.
- Tooling and parsing: XML-based tools, RSS feeds, and content syndication systems that consume your HTML will choke on attributes that violate XML naming rules.
How to fix it
- Check for unresolved template expressions. Inspect the rendered HTML (not your source templates) for leftover placeholders like {{...}}, <%= ... %>, @{...}, or similar patterns.
- Ensure proper server-side rendering. Make sure your templating engine is correctly processing all expressions before the HTML is sent to the client.
- Remove invalid characters from attribute names. If you’re using custom attributes, stick to valid data-* attributes with names consisting only of lowercase letters, digits, and hyphens (after the data- prefix).
- Check for typos. A missing = sign, quote, or space can cause a value to be interpreted as an attribute name.
Examples
Unresolved template placeholder in an attribute
This occurs when a template expression isn’t processed and appears literally in the output:
<!-- ❌ Bad: template syntax rendered as attribute name -->
<div {{classBinding}} id="main">
<p>Hello, world!</p>
</div>
The fix is to ensure the template engine resolves the expression. The rendered output should look like this:
<!-- ✅ Good: attribute properly resolved -->
<div class="container" id="main">
<p>Hello, world!</p>
</div>
Typo causing a value to be parsed as an attribute name
A missing equals sign or quotation mark can cause part of a value to become an attribute name with invalid characters:
<!-- ❌ Bad: missing = sign causes "bold}" to be treated as an attribute -->
<p style"font-weight:bold}" class="intro">Welcome</p>
<!-- ✅ Good: proper attribute syntax -->
<p style="font-weight:bold" class="intro">Welcome</p>
Special characters in custom attribute names
Using invalid characters directly in attribute names will trigger this error:
<!-- ❌ Bad: @ and $ are not valid in attribute names -->
<input @change="update" $value="test">
If you need custom attributes, use the standard data-* pattern:
<!-- ✅ Good: valid data attributes -->
<input data-change="update" data-value="test">
Angular-style bindings in static HTML
Frameworks like Angular use special attribute syntax (e.g., [property] or (event)) that is only valid within the framework’s context and will fail validation if rendered directly:
<!-- ❌ Bad: framework-specific syntax in raw HTML -->
<img [src]="imageUrl" (load)="onLoad()">
If you’re generating static HTML, use standard attributes instead:
<!-- ✅ Good: standard HTML attribute -->
<img src="photo.jpg" alt="A photo">
Every HTML element has a defined set of attributes it accepts. The HTML specification maintains strict rules about which attributes belong on which elements. For example, the href attribute is valid on an <a> element but not on a <div>. The for attribute belongs on <label> and <output> elements but not on <span>. When you place an attribute on an element that doesn’t recognize it, the validator flags the error.
This issue matters for several reasons. First, browsers may silently ignore unrecognized attributes, meaning your code might appear to work but isn’t actually doing anything — leading to hard-to-diagnose bugs. Second, assistive technologies like screen readers rely on valid HTML to correctly interpret page structure and behavior. Invalid attributes can confuse these tools and degrade accessibility. Third, standards-compliant HTML ensures consistent behavior across all browsers and future-proofs your code.
There are several common causes of this error:
- Typos or misspellings — Writing hieght instead of height, or scr instead of src.
- Attributes on the wrong element — Using placeholder on a <div> instead of an <input> or <textarea>.
- Obsolete attributes — Using presentational attributes like align, bgcolor, or border that have been removed from the HTML specification in favor of CSS.
- Framework-specific attributes — Using attributes like ng-click (Angular), v-if (Vue), or @click (Vue shorthand) that aren’t part of standard HTML. These frameworks typically process them before the browser sees them, but the raw HTML won’t validate.
- Custom attributes without the data-* prefix — Inventing your own attributes like tooltip or status without following the data-* convention.
- ARIA attributes with typos — Writing aria-role instead of the correct role, or aria-labelled instead of aria-labelledby.
Examples
Attribute used on the wrong element
The placeholder attribute is only valid on <input> and <textarea> elements:
<!-- ❌ "placeholder" not allowed on "div" -->
<div placeholder="Enter text here">Content</div>
<!-- ✅ Use placeholder on a supported element -->
<input type="text" placeholder="Enter text here">
Obsolete presentational attribute
The align attribute has been removed from most elements in HTML5. Use CSS instead:
<!-- ❌ "align" not allowed on "div" -->
<div align="center">Centered content</div>
<!-- ✅ Use CSS for presentation -->
<div style="text-align: center;">Centered content</div>
Custom attribute without data-* prefix
If you need to store custom data on an element, use the data-* attribute format:
<!-- ❌ "tooltip" not allowed on "span" -->
<span tooltip="More information">Hover me</span>
<!-- ✅ Use a data-* attribute for custom data -->
<span data-tooltip="More information">Hover me</span>
The data-* attributes are specifically designed for embedding custom data. You can access them in JavaScript via the dataset property, e.g., element.dataset.tooltip.
Misspelled attribute
A simple typo can trigger this error:
<!-- ❌ "widht" not allowed on "img" -->
<img src="photo.jpg" widht="300" alt="A photo">
<!-- ✅ Correct the spelling -->
<img src="photo.jpg" width="300" alt="A photo">
ARIA attribute typo
ARIA attributes must match their exact specification names:
<!-- ❌ "aria-labelled" not allowed on "input" -->
<input type="text" aria-labelled="name-label">
<!-- ✅ Use the correct ARIA attribute name -->
<input type="text" aria-labelledby="name-label">
Framework-specific attributes
If you’re using a JavaScript framework and want your source templates to validate, be aware that framework-specific syntax won’t pass validation. In Vue, for example, you can use the data-* equivalent or accept that templates are preprocessed:
<!-- ❌ "v-if" not allowed on "div" -->
<div v-if="isVisible">Hello</div>
<!-- This is expected with Vue templates and is typically not a concern,
since the framework processes these before they reach the browser. -->
When encountering this error, check the MDN Web Docs reference for the element in question to see which attributes it actually supports. This will quickly clarify whether you need to fix a typo, move the attribute to a different element, replace it with CSS, or convert it to a data-* attribute.
Ready to validate your sites?
Start your free trial today.