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.
According to the HTML specification, <meta> elements are metadata content and must appear within the <head> element. When a <meta> tag appears between </head> and <body>, browsers have to error-correct by either ignoring the element or silently relocating it into the head. This can lead to unpredictable behavior — for instance, a <meta charset="utf-8"> tag in the wrong position might not be processed in time, causing character encoding issues. Similarly, a misplaced <meta name="viewport"> could fail to apply on some browsers, breaking your responsive layout.
There are several common causes for this error:
- A
<meta>tag accidentally placed after</head>— perhaps added hastily or through a copy-paste error. - A duplicate
<head>section — if a second<head>block appears in the document, the browser closes the first one implicitly, leaving orphaned<meta>elements in limbo. - An unclosed element inside
<head>— a tag like an unclosed<link>or<script>can confuse the parser, causing it to implicitly close</head>earlier than expected, which pushes subsequent<meta>tags outside the head. - Template or CMS injection — content management systems or templating engines sometimes inject
<meta>tags at incorrect positions in the document.
To fix the issue, inspect your HTML source and ensure every <meta> element is inside a single, properly structured <head> section. Also verify that no elements within <head> are unclosed or malformed, as this can cause the parser to end the head section prematurely.
Examples
Incorrect — <meta> between </head> and <body>
<!DOCTYPE html>
<htmllang="en">
<head>
<title>My Page</title>
<metacharset="utf-8">
</head>
<metaname="viewport"content="width=device-width, initial-scale=1.0">
<body>
<p>Hello, world!</p>
</body>
</html>
The <meta name="viewport"> tag is outside <head>, triggering the validation error.
Incorrect — duplicate <head> sections
<!DOCTYPE html>
<htmllang="en">
<head>
<title>My Page</title>
<metacharset="utf-8">
</head>
<head>
<metaname="description"content="A sample page">
</head>
<body>
<p>Hello, world!</p>
</body>
</html>
The second <head> block is invalid. The browser ignores it, leaving the <meta name="description"> element stranded between </head> and <body>.
Incorrect — unclosed element forces early head closure
<!DOCTYPE html>
<htmllang="en">
<head>
<title>My Page</title>
<metacharset="utf-8">
<linkrel="stylesheet"href="style.css">
<div>
<metaname="viewport"content="width=device-width, initial-scale=1.0">
</head>
<body>
<p>Hello, world!</p>
</body>
</html>
The <div> is not valid inside <head>, so the parser implicitly closes the head section when it encounters it. The subsequent <meta> tag ends up outside <head>.
Correct — all <meta> elements inside <head>
<!DOCTYPE html>
<htmllang="en">
<head>
<metacharset="utf-8">
<metaname="viewport"content="width=device-width, initial-scale=1.0">
<metaname="description"content="A sample page">
<title>My Page</title>
<linkrel="stylesheet"href="style.css">
</head>
<body>
<p>Hello, world!</p>
</body>
</html>
All <meta> elements are properly contained within a single <head> section. Note that <meta charset="utf-8"> should ideally be the very first element in <head> so the browser knows the encoding before processing any other content.
The HTML specification defines a strict content model for tables. A <table> element can only contain <caption>, <colgroup>, <thead>, <tbody>, <tfoot>, and <tr> as direct children. A <tr> element can only contain <td> and <th> elements. When text or inline elements like <span>, <a>, or <strong> appear directly inside any of these table-structural elements, they violate the content model and trigger this validation error.
This matters for several reasons. Browsers handle misplaced table content inconsistently — some will silently move the text before the table, others may discard it, and some may attempt to create anonymous table cells. This leads to unpredictable layouts across browsers and devices. Screen readers rely on correct table structure to navigate cells and announce row/column relationships, so misplaced content can confuse assistive technology users. Additionally, any text placed directly inside a <tr> but outside a cell is technically not part of any row/column structure, making it semantically meaningless in the table context.
Common causes of this error include:
- Accidentally placing text between
<tr>and<td>tags - Placing a heading or paragraph directly inside a
<table>(use<caption>instead for table titles) - Using non-breaking spaces (
) or other characters directly inside<table>or<tr>for spacing - Incorrectly nesting non-table elements like
<div>,<p>, or<form>as direct children of<table>or<tr>
Examples
Text directly inside a table row
<!-- ❌ Wrong: text is a direct child of <tr> -->
<table>
<tr>
Hello World
<td>First Cell</td>
<td>Second Cell</td>
</tr>
</table>
<!-- ✅ Fixed: text is wrapped in a <td> -->
<table>
<tr>
<td>Hello World</td>
<td>First Cell</td>
<td>Second Cell</td>
</tr>
</table>
Text directly inside a table element
<!-- ❌ Wrong: heading and spacing text placed directly in <table> -->
<table>
Monthly Sales Report
<tr>
<th>Month</th>
<th>Revenue</th>
</tr>
</table>
<!-- ✅ Fixed: use <caption> for table titles -->
<table>
<caption>Monthly Sales Report</caption>
<tr>
<th>Month</th>
<th>Revenue</th>
</tr>
</table>
Non-breaking spaces used for spacing
<!-- ❌ Wrong: directly inside <tr> -->
<table>
<tr>
<td>Data</td>
</tr>
</table>
<!-- ✅ Fixed: use CSS for spacing, or place content in a cell -->
<table>
<tr>
<td>Data</td>
</tr>
</table>
Inline elements directly inside a table
<!-- ❌ Wrong: <a> tag directly inside <table> -->
<table>
<ahref="/details">View details</a>
<tr>
<td>Item 1</td>
<td>$10.00</td>
</tr>
</table>
<!-- ✅ Fixed: move the link into a cell or outside the table -->
<table>
<tr>
<td>Item 1</td>
<td>$10.00</td>
<td><ahref="/details">View details</a></td>
</tr>
</table>
Content between table sections
<!-- ❌ Wrong: text between <thead> and <tbody> -->
<table>
<thead>
<tr>
<th>Name</th>
</tr>
</thead>
No results found
<tbody>
<tr>
<td>—</td>
</tr>
</tbody>
</table>
<!-- ✅ Fixed: place the message inside a cell -->
<table>
<thead>
<tr>
<th>Name</th>
</tr>
</thead>
<tbody>
<tr>
<td>No results found</td>
</tr>
</tbody>
</table>
The HTML specification defines <p> as an element that can only contain phrasing content — essentially inline elements like <span>, <a>, <strong>, <em>, and text. When the parser encounters a block-level element (such as <div>, <ul>, <ol>, <table>, <blockquote>, or another <p>) inside a <p>, it automatically closes the <p> element before the block-level element. This implicit closure means that a </p> tag appearing after the block-level element has no matching open <p> to close.
Understanding this behavior is important because it can lead to unexpected DOM structures. Browsers will still render the page, but the actual DOM tree may differ from what you intended, potentially affecting CSS styling and JavaScript DOM manipulation. It also signals structural problems in your markup that can hurt accessibility, since assistive technologies rely on a well-formed document tree.
There are three common causes of this error:
- Duplicate closing tags — A simple typo where
</p>appears twice. - Block-level elements inside
<p>— Nesting a<div>,<ul>,<table>, or similar element inside a paragraph causes the browser to implicitly close the<p>before the block element, leaving the explicit</p>orphaned. - Mismatched or misordered tags — Other nesting errors that leave a
</p>without a corresponding opening tag.
Examples
Duplicate closing tag
The simplest case — an extra </p> with no matching opening tag:
<!-- ❌ Bad: duplicate end tag -->
<p>Some text.</p></p>
<!-- ✅ Good: single end tag -->
<p>Some text.</p>
Block-level element nested inside a paragraph
This is the most common and least obvious cause. When a <div> (or other block-level element) appears inside a <p>, the parser closes the <p> implicitly:
<!-- ❌ Bad: div inside p causes implicit closure -->
<p>
Some introductory text.
<divclass="highlight">Highlighted content</div>
</p>
The parser interprets this as:
<p>Some introductory text.</p>
<divclass="highlight">Highlighted content</div>
</p> ← orphaned end tag, triggers the error
To fix this, either replace the outer <p> with a <div>, or replace the inner <div> with a <span>:
<!-- ✅ Good: use div as the outer container -->
<div>
<p>Some introductory text.</p>
<divclass="highlight">Highlighted content</div>
</div>
<!-- ✅ Good: use span instead of div for inline styling -->
<p>
Some introductory text.
<spanclass="highlight">Highlighted content</span>
</p>
List nested inside a paragraph
Lists are block-level elements and cannot be nested inside <p>:
<!-- ❌ Bad: ul inside p -->
<p>
Choose one of the following:
<ul>
<li>Option A</li>
<li>Option B</li>
</ul>
</p>
<!-- ✅ Good: separate the paragraph and list -->
<p>Choose one of the following:</p>
<ul>
<li>Option A</li>
<li>Option B</li>
</ul>
Orphaned end tag from a deleted opening tag
Sometimes during editing, the opening <p> gets accidentally removed:
<!-- ❌ Bad: missing opening tag -->
<div>
Some text that lost its opening tag.
</p>
</div>
<!-- ✅ Good: restore the opening tag or remove the closing tag -->
<div>
<p>Some text with its opening tag restored.</p>
</div>
When debugging this error, look at the line number reported by the validator and trace backward. If you see a </p> on that line, check whether there's a valid opening <p> for it, and verify that no block-level elements between the opening and closing tags could have caused an implicit closure.
The HTML specification requires that attributes on an element be separated by one or more ASCII whitespace characters. When you omit the space — typically between a closing quote of one attribute's value and the name of the next attribute — the browser may struggle to determine where one attribute ends and the next begins. While some browsers may attempt to parse the element correctly, the behavior is not guaranteed and can lead to unexpected results.
This issue commonly occurs when editing HTML by hand, especially when copying and pasting attributes from different sources or when a template engine concatenates attribute strings without proper spacing. It can also happen when a closing quote is immediately followed by another attribute name, making the markup difficult to read and maintain.
Beyond validation, missing spaces between attributes hurt code readability. Other developers (or your future self) scanning the markup may misread attribute boundaries, leading to bugs that are hard to track down. Consistent spacing also ensures that assistive technologies and browser parsers interpret your elements exactly as intended.
How to Fix It
Go through your HTML and ensure every attribute is separated from adjacent attributes by at least one space character. Pay special attention to:
- Attributes placed immediately after a quoted value (e.g.,
"value"classshould be"value" class). - Attributes placed after unquoted values, which can be even more ambiguous.
- Dynamically generated markup from template engines or JavaScript, where concatenation might drop whitespace.
Examples
Incorrect: No Space Between Attributes
<ahref="page.php"class="big">link</a>
Here, class is placed directly after the closing quote of the href value with no space in between. The validator will flag this as an error.
Correct: Space Between Attributes
<ahref="page.php"class="big">link</a>
A single space between "page.php" and class resolves the issue.
Incorrect: Multiple Missing Spaces
<imgsrc="photo.jpg"alt="A sunset"width="600"height="400">
All four attributes run together without any whitespace separating them.
Correct: All Attributes Properly Spaced
<imgsrc="photo.jpg"alt="A sunset"width="600"height="400">
Correct: Using Line Breaks as Whitespace
When an element has many attributes, you can use newlines for separation, which also improves readability:
<img
src="photo.jpg"
alt="A sunset"
width="600"
height="400">
Newlines and indentation count as valid whitespace between attributes and are perfectly acceptable in HTML. This multi-line style is especially helpful for elements with numerous attributes, such as <input> fields in forms or components with data attributes.
When the W3C HTML Validator reports "No X element in scope but a X end tag seen," it means the parser encountered a closing tag for an element that was never opened, or that was already closed. The validator tracks which elements are currently "in scope" — meaning which opening tags are still waiting for their corresponding closing tags. When a closing tag appears that doesn't match any open element in scope, this error is raised.
This issue matters for several reasons. First, it indicates malformed HTML that browsers must guess how to interpret, potentially leading to inconsistent rendering across different browsers. Second, broken document structure can confuse assistive technologies like screen readers, harming accessibility. Third, stray end tags can inadvertently affect the DOM tree in unexpected ways, making your CSS and JavaScript behave unpredictably.
There are several common causes:
- Closing a tag twice — the most frequent cause, where a copy-paste error or oversight leads to a duplicate closing tag.
- Missing the opening tag — the corresponding opening tag was accidentally deleted or never added.
- Misnested tags — elements overlap instead of being properly nested, causing the browser to auto-close one element, which makes a later closing tag orphaned.
- Typos in tag names — an opening tag has a different name than the closing tag (e.g.,
<div>opened but</dvi>used to close it, though this usually triggers a different error).
To fix this, carefully review the HTML around the flagged line. Trace each closing tag back to its opening tag. Using a code editor with bracket/tag matching can make this much easier. Remove any duplicate end tags, add any missing opening tags, or fix nesting order as needed.
Examples
Closing a tag twice
This is the most common cause. The </p> tag appears twice:
<!-- ❌ Bad: duplicate closing tag -->
<p>Hello world</p></p>
Remove the extra closing tag:
<!-- ✅ Good -->
<p>Hello world</p>
Missing opening tag
Here a </span> end tag has no corresponding <span>:
<!-- ❌ Bad: no opening <span> -->
<div>
Some text</span>
</div>
Either add the missing opening tag or remove the stray end tag:
<!-- ✅ Good: opening tag added -->
<div>
<span>Some text</span>
</div>
Misnested tags
When elements overlap instead of nesting properly, the browser auto-closes the inner element, leaving a later closing tag orphaned:
<!-- ❌ Bad: <b> and <i> overlap -->
<p><b>Bold and <i>italic</b> text</i></p>
In this case, the browser closes the <i> when it encounters </b> (since <i> is inside <b>), so the later </i> has no element in scope. Fix it by nesting correctly:
<!-- ✅ Good: proper nesting -->
<p><b>Bold and <i>italic</i></b><i>text</i></p>
Stray end tag after auto-closed element
Some elements like <p> are automatically closed by certain subsequent tags. This can lead to orphaned end tags:
<!-- ❌ Bad: <p> is auto-closed by <div>, leaving </p> orphaned -->
<p>Some text
<div>A block element</div>
</p>
The <p> element cannot contain a <div>, so the browser implicitly closes the <p> before the <div>. The final </p> then has no <p> in scope. Restructure the markup:
<!-- ✅ Good: separate elements -->
<p>Some text</p>
<div>A block element</div>
Tip: use editor tooling
Most modern code editors can highlight matching opening and closing tags. If you see a closing tag without a highlighted match, that's likely the source of this error. Extensions for VS Code, Sublime Text, and other editors can also auto-format and validate your HTML as you type, catching these issues early.
The HTML specification defines a strict structure for documents: the <html> element contains a <head> and a <body>, and all visible page content must live inside the <body>. Once the browser encounters the </body> closing tag, it expects nothing but optional whitespace (spaces, tabs, newlines) before the closing </html> tag. Any non-space characters in that gap — whether text, HTML elements, script tags, or even stray punctuation — are considered outside the document body and trigger this validation error.
Why this matters
- Standards compliance: The HTML living standard (WHATWG) is clear that content after
</body>is not valid. W3C validation will flag this as an error. - Unpredictable rendering: Browsers use error recovery to handle misplaced content, and different browsers may handle it differently. Most will implicitly reopen the
<body>and move the content inside it, but relying on this behavior is fragile and unpredictable. - Accessibility: Screen readers and assistive technologies parse the DOM structure. Content that lands in unexpected places due to error recovery may be announced out of order or missed entirely.
- Maintainability: Stray content after
</body>is often a sign of a code organization problem — a misplaced include, a template rendering issue, or an accidental paste — and cleaning it up improves long-term maintainability.
Common causes
- Accidental text or typos after the closing
</body>tag. - Script or analytics tags mistakenly placed between
</body>and</html>instead of at the end of the<body>. - Server-side template engines appending content after the body in a layout file.
- Comments or debugging output left behind after
</body>.
Examples
Stray text after </body>
This triggers the validation error because the text this is invalid appears after </body>:
<!DOCTYPE html>
<htmllang="en">
<head>
<title>Example</title>
</head>
<body>
<p>Content goes here.</p>
</body> this is invalid
</html>
Move all content inside the <body>:
<!DOCTYPE html>
<htmllang="en">
<head>
<title>Example</title>
</head>
<body>
<p>Content goes here.</p>
</body>
</html>
Script placed outside the body
A common mistake is placing a <script> tag between </body> and </html>:
<!DOCTYPE html>
<htmllang="en">
<head>
<title>Example</title>
</head>
<body>
<p>Main content</p>
</body>
<scriptsrc="analytics.js"></script>
</html>
Place the script at the end of the <body> instead:
<!DOCTYPE html>
<htmllang="en">
<head>
<title>Example</title>
</head>
<body>
<p>Main content</p>
<scriptsrc="analytics.js"></script>
</body>
</html>
Stray HTML element after </body>
Sometimes an element like a <div> ends up after the body due to a template issue:
<!DOCTYPE html>
<htmllang="en">
<head>
<title>Example</title>
</head>
<body>
<main>Page content</main>
</body>
<divid="overlay"></div>
</html>
Move it inside <body>:
<!DOCTYPE html>
<htmllang="en">
<head>
<title>Example</title>
</head>
<body>
<main>Page content</main>
<divid="overlay"></div>
</body>
</html>
The fix is always the same: ensure that everything between </body> and </html> is either whitespace or nothing at all. All content — text, elements, scripts, and comments — belongs inside the <body> element.
The <!DOCTYPE html> declaration is required by the HTML specification as the first non-whitespace content in any HTML document. It instructs the browser to render the page in standards mode, which means the browser follows modern CSS and HTML specifications faithfully. When the doctype is missing or preceded by other content, browsers fall back to quirks mode — a legacy rendering mode that emulates old browser behaviors and can cause inconsistent layout, box model differences, and other subtle bugs across browsers.
This error is triggered in several common scenarios:
- The
<!DOCTYPE html>declaration is completely absent from the file. - Text, HTML tags, or other non-space characters appear before the doctype.
- A Byte Order Mark (BOM) or invisible characters were inadvertently inserted before the doctype by a text editor.
- Server-side code (such as PHP or template includes) outputs content before the doctype.
- An XML processing instruction like
<?xml version="1.0"?>precedes the doctype (this is unnecessary in HTML5).
Beyond triggering quirks mode, a missing or misplaced doctype breaks standards compliance and can cause real-world rendering issues. For example, in quirks mode, box-sizing defaults differ, table elements don't inherit font sizes properly, and percentage-based heights may not resolve correctly. These problems can be difficult to debug because they only manifest in certain browsers or under specific conditions.
How to Fix
- Open your HTML file and make sure the very first line is
<!DOCTYPE html>with nothing before it — no whitespace characters, no text, no comments, and no BOM. - Check for invisible characters. If you copied content from another source or use a text editor that inserts a BOM, open the file in a hex editor or use your editor's "show invisible characters" feature to confirm nothing precedes the doctype.
- Check server-side scripts. If you use PHP, ASP, or a templating engine, make sure no output (including blank lines or spaces outside script tags) is emitted before
<!DOCTYPE html>. - Remove legacy doctypes or XML declarations. In HTML5, the only doctype you need is
<!DOCTYPE html>. Older doctypes like<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" ...>are valid but unnecessary, and XML processing instructions should not be used in HTML documents.
Examples
Missing doctype
This document has no doctype at all, which triggers the error:
<htmllang="en">
<head>
<title>My Page</title>
</head>
<body>
<p>Hello, world!</p>
</body>
</html>
Text before the doctype
Stray text or content before the declaration also triggers the error:
Welcome to my site!
<!DOCTYPE html>
<htmllang="en">
<head>
<title>My Page</title>
</head>
<body>
<p>Hello, world!</p>
</body>
</html>
PHP output before the doctype
A common server-side issue where whitespace or output leaks before the doctype:
<?phpinclude'config.php';?>
<!DOCTYPE html>
<html lang="en">
<head>
<title>My Page</title>
</head>
<body>
<p>Hello, world!</p>
</body>
</html>
If config.php has a closing ?> followed by a blank line, that blank line gets sent as output before the doctype. Ensure included files don't produce unintended output.
Correct document
The doctype must be the absolute first thing in the file:
<!DOCTYPE html>
<htmllang="en">
<head>
<title>My Page</title>
</head>
<body>
<p>Hello, world!</p>
</body>
</html>
Note that the doctype declaration is case-insensitive — <!DOCTYPE html>, <!doctype html>, and <!Doctype Html> are all valid. However, <!DOCTYPE html> is the conventional form recommended by the HTML living standard. A single blank line or whitespace-only line before the doctype won't trigger this specific error (since the validator message specifies "non-space characters"), but it's best practice to avoid any content before the declaration to keep your document clean and unambiguous.
The doctype declaration tells the browser which version of HTML the document is written in and determines how the browser renders the page. Older HTML versions required long, complex doctype strings that referenced Document Type Definitions (DTDs). With the introduction of HTML5, the doctype was simplified to just <!DOCTYPE html>, which is now the only recommended doctype for modern web pages.
When a browser encounters an obsolete doctype—or no doctype at all—it may switch into "quirks mode," a legacy rendering mode that emulates old browser behavior. In quirks mode, CSS box models, layout calculations, and other rendering behaviors can differ significantly from standards mode, leading to inconsistent appearance across browsers. Using the modern <!DOCTYPE html> ensures the browser operates in "standards mode," giving you predictable and consistent rendering.
Beyond rendering concerns, using an obsolete doctype means your document is referencing outdated specifications. Modern HTML validators, including the W3C Nu HTML Checker, are designed to validate against the current HTML Living Standard. Legacy doctypes may cause the validator to misinterpret your markup or report errors that don't apply, making it harder to maintain clean, valid code.
To fix this, simply replace whatever doctype declaration appears at the top of your HTML file with <!DOCTYPE html>. This single change is backward-compatible—it works in all modern browsers and even in Internet Explorer 6 and later. No DTD reference or version number is needed.
Examples
Obsolete doctypes that trigger this issue
These are common legacy doctypes that the validator will flag:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
"http://www.w3.org/TR/html4/loose.dtd"
<html>
<head>
<title>My Page</title>
</head>
<body>
<p>Hello world</p>
</body>
</html>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"
<htmlxmlns="http://www.w3.org/1999/xhtml">
<head>
<title>My Page</title>
</head>
<body>
<p>Hello world</p>
</body>
</html>
Correct HTML5 doctype
Replace the obsolete doctype with the simple, modern declaration:
<!DOCTYPE html>
<htmllang="en">
<head>
<metacharset="utf-8">
<title>My Page</title>
</head>
<body>
<p>Hello world</p>
</body>
</html>
Key points about the modern doctype
- It must appear on the very first line of the document, before the
<html>tag. No comments or whitespace should precede it. - The declaration is case-insensitive—
<!DOCTYPE html>,<!doctype html>, and<!Doctype Html>are all valid, though lowercase or uppercase conventions are most common. - Unlike older doctypes, it does not reference a DTD URL or version number.
- Adding a
langattribute to the<html>element and a<meta charset="utf-8">tag in the<head>are best practices that pair well with the modern doctype for a fully standards-compliant document.
In CSS, most numeric values represent a length, time, angle, or other dimension, and the unit tells the browser how to interpret the number. Writing padding: 20 is ambiguous — does it mean 20 pixels, 20 ems, or 20 percent? Without a unit, the browser has no way to determine the author's intent, so it treats the value as invalid and discards the entire declaration. This can lead to broken layouts, missing spacing, or other visual problems that are difficult to debug.
The only exception to this rule is 0. Zero pixels is the same as zero ems, zero rems, or zero percent — it's always nothing regardless of the unit. Because of this, the CSS specification allows 0 to be written without a unit. In fact, omitting the unit on zero values is considered a best practice for cleaner, more concise CSS.
This validation error commonly appears in inline style attributes, which is where the W3C HTML Validator encounters it. However, the same rule applies to CSS written in <style> elements and external stylesheets.
Common causes of this error include:
- Accidentally forgetting to type the unit after a number.
- Copy-pasting values from design tools that don't include units.
- Confusing CSS with JavaScript's
element.style.width = "50"pattern (where some APIs accept unitless numbers). - Assuming that
pxis the default unit (it is not — there is no default unit in CSS).
Examples
Incorrect — missing units on numeric values
<divstyle="margin:10;padding:20;width:300;">
Content here
</div>
The validator will report errors for 10, 20, and 300 because none of them have units.
Correct — units specified on all non-zero values
<divstyle="margin:10px;padding:20px;width:300px;">
Content here
</div>
Correct — zero without a unit
<divstyle="margin:0;padding:0;border:0;">
No units needed for zero
</div>
Correct — mixing zero and non-zero values
<divstyle="margin:010px020px;">
Top and bottom margins are zero, sides have units
</div>
Correct — using other unit types
<pstyle="font-size:1.2em;line-height:1.5em;margin-bottom:2rem;">
Text with relative units
</p>
Note that line-height is a special case in CSS — it actually accepts a unitless number (like 1.5) as a valid value, where the number acts as a multiplier of the element's font size. This is not the same as a missing unit; it's a deliberately unitless ratio defined in the specification. Most other properties do not have this behavior.
The aria-label attribute defines an accessible name for an element, providing a text label that assistive technologies (like screen readers) can announce to users. It's especially useful when there's no visible text label on screen, such as for icon buttons or navigation landmarks. However, aria-label doesn't work on every HTML element — it only has an effect on elements that have roles which support name from author.
According to the ARIA specification, aria-label is designed to work with interactive elements (like <button>, <a> with href, <input>), landmark and structural roles (like <nav>, <section>, <form>), images (<img>), and elements with explicit widget or landmark roles. When applied to generic elements that have no role (or only the generic implicit role), such as <div> or <span>, the attribute is effectively ignored by assistive technologies. This means users of screen readers won't hear the label, defeating its purpose entirely.
This matters for several reasons:
- Accessibility: Developers may believe they've provided an accessible label when they haven't. Users relying on screen readers will miss the information entirely.
- Standards compliance: The ARIA in HTML specification explicitly prohibits
aria-labelon elements where it has no effect, and the W3C validator flags this as a warning. - Maintainability: Meaningless attributes add noise to the codebase and can mislead future developers about the element's purpose.
To fix this warning, either move the aria-label to an appropriate element, add an explicit role attribute that supports naming, use aria-label on a different wrapping element that supports it, or replace the approach with visible text or a visually-hidden CSS class.
Examples
Incorrect: aria-label on a <div> with no role
A plain <div> has the implicit generic role, which does not support naming. The aria-label will be ignored.
<divaria-label="User profile section">
<imgsrc="avatar.jpg"alt="User avatar">
<p>Jane Doe</p>
</div>
Correct: Use a landmark element or add an appropriate role
Wrapping the content in a <section> element gives it the region landmark role (when labeled), making aria-label valid and meaningful:
<sectionaria-label="User profile section">
<imgsrc="avatar.jpg"alt="User avatar">
<p>Jane Doe</p>
</section>
Alternatively, add an explicit role to the <div>:
<divrole="region"aria-label="User profile section">
<imgsrc="avatar.jpg"alt="User avatar">
<p>Jane Doe</p>
</div>
Incorrect: aria-label on a <span>
<spanaria-label="Warning">⚠️</span>
The <span> has the generic role and does not support aria-label.
Correct: Use an appropriate role or element
<spanrole="img"aria-label="Warning">⚠️</span>
Incorrect: aria-label on a <p> element
<paria-label="Important note">Please read the terms carefully.</p>
The <p> element has the paragraph role, which does not support naming via aria-label.
Correct: Use visible text or restructure
If you need to provide additional context, consider restructuring:
<sectionaria-label="Important note">
<p>Please read the terms carefully.</p>
</section>
Or simply ensure the visible text is sufficiently descriptive, removing the unnecessary aria-label:
<p><strong>Important note:</strong> Please read the terms carefully.</p>
Common elements where aria-label works correctly
<!-- Interactive elements -->
<buttonaria-label="Close dialog">✕</button>
<ahref="/home"aria-label="Go to homepage">🏠</a>
<inputtype="search"aria-label="Search the site">
<!-- Landmark elements -->
<navaria-label="Main navigation">
<ul>
<li><ahref="/">Home</a></li>
</ul>
</nav>
<formaria-label="Newsletter signup">
<inputtype="email"aria-label="Email address">
<button>Subscribe</button>
</form>
<!-- Images -->
<imgsrc="chart.png"aria-label="Sales data for Q4 2024">
Quick reference: elements that do not support aria-label
Without an explicit role, aria-label will be flagged on these common elements: <div>, <span>, <p>, <b>, <i>, <em>, <strong>, <small>, <code>, <pre>, <blockquote>, <ul>, <ol>, <li>, <dl>, <dt>, <dd>, <table>, <thead>, <tbody>, and <tfoot>, among others. If you need aria-label on one of these, consider whether a more semantic element or an explicit ARIA role is the right solution first.
The doctype declaration tells browsers which version of HTML a page uses and, critically, determines whether the browser renders the page in standards mode or quirks mode. Standards mode follows current CSS and HTML specifications, while quirks mode emulates the buggy behavior of older browsers from the late 1990s and early 2000s. When the W3C validator reports "Quirky doctype," it means your document's doctype is one that is known to trigger quirks mode—and in the context of modern HTML, this is always undesirable.
Several legacy doctypes can trigger this warning, including:
<!DOCTYPE html public "-//W3C//DTD HTML 4.0 Transitional//EN">(without a system identifier URL)<!DOCTYPE html public "-//W3C//DTD HTML 3.2 Final//EN">- Malformed or incomplete doctype declarations
- Doctypes with unexpected casing or extra whitespace in certain positions
The HTML Living Standard (maintained by WHATWG) specifies that the doctype must be <!DOCTYPE html>. This short declaration is case-insensitive but is conventionally written in uppercase. It exists solely to trigger standards mode—it does not reference a DTD because HTML5 is not based on SGML.
Why This Matters
Inconsistent rendering: In quirks mode, browsers alter how they calculate the box model, handle table sizing, apply inline element dimensions, and more. A page that looks correct in one browser may break in another because each browser's quirks mode emulates different legacy behaviors.
Accessibility and interoperability: Screen readers and other assistive technologies rely on predictable DOM behavior. Quirks mode can cause unexpected layout shifts and content reflow that degrade the experience for users of assistive technology.
Standards compliance: Modern HTML validators expect the HTML5 doctype. Using a legacy doctype signals that the document is not following current web standards, which can also affect SEO tools and automated audits.
How to Fix It
- Open your HTML file and locate the very first line.
- Replace whatever doctype declaration is there with
<!DOCTYPE html>. - Ensure nothing appears before the doctype—no whitespace, no comments, no byte order marks (BOMs). Any content before the doctype can also trigger quirks mode in some browsers.
- Verify that the rest of your document uses valid HTML5 syntax (e.g., the
<html>tag includes alangattribute, the<head>contains a<meta charset>declaration and a<title>element).
Examples
Incorrect: Legacy doctype triggering quirks mode
<!DOCTYPE html public "-//W3C//DTD HTML 4.0 Transitional//EN">
<html>
<head>
<title>My Page</title>
</head>
<body>
<p>This page renders in quirks mode.</p>
</body>
</html>
This HTML 4.0 Transitional doctype without a system identifier URL is a known quirks-mode trigger.
Incorrect: Another common quirky doctype
<!DOCTYPE html public "-//W3C//DTD HTML 3.2 Final//EN">
<html>
<head>
<title>My Page</title>
</head>
<body>
<p>This older doctype also triggers quirks mode.</p>
</body>
</html>
Correct: Standard HTML5 doctype
<!DOCTYPE html>
<htmllang="en">
<head>
<metacharset="utf-8">
<title>My Page</title>
</head>
<body>
<p>This page renders in standards mode.</p>
</body>
</html>
The simple <!DOCTYPE html> declaration ensures standards mode across all modern browsers. It is the only doctype you should use for new HTML documents, and it is also fully backward-compatible with older browsers—even Internet Explorer 6 renders in standards mode with this doctype.
When the HTML parser encounters a tag's attributes, it expects a specific structure: an attribute name, followed by an equals sign, followed by a quoted value. If a quote character appears where the parser expects an attribute name, it means something has gone wrong in the syntax earlier in the tag. The validator flags this as Quote """ in attribute name and suggests that a matching quote is likely missing somewhere before.
This issue matters because browsers will try to recover from the malformed HTML in unpredictable ways. One browser might ignore the attribute entirely, while another might merge it with adjacent text. This can lead to broken styling, missing functionality, or elements that behave differently across browsers. For accessibility, malformed attributes can prevent assistive technologies from correctly interpreting element semantics and roles.
There are several common causes for this error:
- Missing equals sign between an attribute name and its value, causing the parser to treat the opening quote as part of the attribute name.
- Extra closing quote that prematurely ends an attribute value, leaving subsequent quotes orphaned.
- Unescaped quotes inside attribute values, which break the quoting structure of the entire tag.
- Copy-paste errors that introduce curly/smart quotes (
"") or extra quote characters. - A missing closing quote on a previous attribute, causing the parser to consume subsequent attributes as part of that value.
Examples
Missing equals sign
The most common cause. Without the =, the parser sees class as one attribute and then encounters " where it expects another attribute name.
❌ Incorrect:
<pclass"news">This paragraph has broken markup.</p>
✅ Fixed:
<pclass="news">This paragraph has correct markup.</p>
Missing closing quote on a previous attribute
When a quote is left unclosed, everything after it—including other attributes—gets consumed as part of the value, until the parser eventually encounters another quote in an unexpected position.
❌ Incorrect:
<ahref="/about class="link">About us</a>
Here, the href value is never closed. The parser reads /about class= as the href value, then encounters "link" in an unexpected context.
✅ Fixed:
<ahref="/about"class="link">About us</a>
Extra quote character
A stray extra quote breaks the pairing of subsequent quotes.
❌ Incorrect:
<divid=""main" class="container">Content</div>
✅ Fixed:
<divid="main"class="container">Content</div>
Unescaped quotes inside an attribute value
If the attribute value itself contains a double quote, it must be escaped as " or the attribute must use single quotes instead.
❌ Incorrect:
<inputtype="text"value="She said "hello"">
✅ Fixed (using "):
<inputtype="text"value="She said "hello"">
✅ Also fixed (using single quotes for the attribute):
<inputtype="text"value='She said "hello"'>
Smart/curly quotes from copy-paste
Word processors and rich-text editors often convert straight quotes to curly quotes, which are not valid for HTML attribute delimiters.
❌ Incorrect:
<pclass="highlight">Styled text</p>
✅ Fixed:
<pclass="highlight">Styled text</p>
How to debug this issue
When you see this error, the validator usually points to a specific line number, but the root cause may be on that line or earlier in the same tag. Follow these steps:
- Go to the line indicated by the validator and examine the full tag.
- Check every attribute on that element for proper
name="value"syntax. - Count the quotes — each attribute value should have exactly one opening and one closing quote.
- Look for missing
=signs between attribute names and their values. - Search for curly quotes (
",",',') and replace them with straight quotes ("or'). - If the line looks correct, check the preceding lines — an unclosed quote on a previous element can cascade errors into later markup.
When you submit a URL to the W3C HTML Validator, it attempts to fetch the page from your server just like a browser would. If the server doesn't respond within the validator's timeout window, the validation process is aborted and you see a "Read timed out" error. No HTML checking occurs at all — the validator never received any markup to analyze.
Common Causes
Several things can trigger this timeout:
- Slow server response: Your server may be under heavy load, running slow database queries, or experiencing resource constraints that delay the response.
- Large page size: Pages with extremely large HTML output can take too long to transmit within the allowed time.
- Geographic latency: The W3C Validator's servers may be geographically distant from your server, adding network delay.
- Firewall or security rules: A firewall, CDN, or WAF (Web Application Firewall) may be blocking or throttling the validator's requests, treating them as bot traffic.
- DNS resolution issues: Your domain's DNS may be slow to resolve or temporarily unavailable.
- Server downtime: The server may simply be offline or unreachable at the time of validation.
How to Fix It
1. Use Direct Input Instead
The most reliable workaround is to copy your page's HTML source and paste it directly into the validator. This bypasses the network fetch entirely.
- Open your page in a browser.
- View the page source (usually
Ctrl+UorCmd+U). - Copy the entire HTML.
- Go to the W3C Validator and select the Validate by Direct Input tab.
- Paste the HTML and click Check.
2. Upload the File
If you have the HTML file locally, use the Validate by File Upload tab to upload it directly. This also avoids any network timeout issues.
3. Fix Server-Side Issues
If you need URL-based validation (for example, in a CI/CD pipeline), address the underlying server problem:
- Check server health: Ensure your server is running and responding to requests within a reasonable time (under a few seconds).
- Whitelist the validator: If you use a WAF or rate limiter, ensure requests from the W3C Validator's user agent are not being blocked or delayed.
- Reduce page size: If your HTML output is extremely large, consider whether it can be simplified or paginated.
- Try again later: Temporary network issues or server load spikes may resolve on their own. Simply retrying after a few minutes often works.
4. Run a Local Validator
For frequent validation or automated workflows, consider running the Nu Html Checker locally. This eliminates network dependencies entirely:
java-jarvnu.jaryour-page.html
Examples
Triggering the Issue
Submitting a URL where the server is slow or unreachable:
https://example-slow-server.com/heavy-page.html
The validator returns:
Readtimedout
No HTML validation results are provided.
Workaround: Validate by Direct Input
Instead of submitting the URL, paste the HTML source directly:
<!DOCTYPE html>
<htmllang="en">
<head>
<metacharset="utf-8">
<title>My Page</title>
</head>
<body>
<h1>Hello, world!</h1>
<p>This content is validated without fetching from a server.</p>
</body>
</html>
This gives you full validation results regardless of server availability or response time.
Summary
The "Read timed out" error is purely a network/server issue, not an HTML quality issue. Your markup might be perfectly valid — the validator just can't reach it. Use direct input or file upload as an immediate workaround, and investigate server-side causes if you specifically need URL-based validation.
The HTML specification requires that every row in a table has at least one cell that starts on that row. A "cell beginning on a row" means a <td> or <th> element that is directly placed in that <tr>, as opposed to a cell that merely spans into it via rowspan from an earlier row. When the validator encounters a row with no cells beginning on it, it flags the error because the row is structurally meaningless — it contributes nothing to the table's data model.
This issue commonly arises in two scenarios:
- Empty
<tr>elements — A<tr>that contains no<td>or<th>children at all. This sometimes appears when developers use empty rows for visual spacing, or when content management systems generate leftover markup. - Rows fully covered by
rowspan— When cells in preceding rows userowspanvalues large enough to span over an entire subsequent row, that subsequent row ends up with no cells beginning on it, even if it technically "has" cells passing through it.
This matters for several reasons. Screen readers and other assistive technologies rely on a well-formed table structure to navigate cells and announce their content. An empty or fully-spanned row confuses this navigation. Browsers may also handle malformed tables inconsistently, leading to unexpected rendering. Ensuring every row has at least one cell that begins on it keeps your tables semantically correct and accessible.
How to fix it
- Remove empty rows. If a
<tr>has no cells and serves no purpose, delete it entirely. - Add cells to the row. If the row is intentional, populate it with
<td>or<th>elements (they can be empty if needed). - Adjust
rowspanvalues. If previous cells span too many rows, reduce theirrowspanso that every row still has at least one cell of its own. - Use CSS for spacing. If empty rows were used for visual spacing, use CSS
margin,padding, orborder-spacinginstead.
Note that self-closing <tr /> elements are treated the same as <tr></tr> — they produce an empty row and will trigger this error.
Examples
Empty row in <tbody> (incorrect)
<table>
<tbody>
<tr>
</tr>
<tr>
<td>Data</td>
</tr>
</tbody>
</table>
The first <tr> has no cells, so the validator reports that row 1 of the <tbody> row group has no cells beginning on it.
Empty row removed (correct)
<table>
<tbody>
<tr>
<td>Data</td>
</tr>
</tbody>
</table>
Row fully covered by rowspan (incorrect)
<table>
<tbody>
<tr>
<tdrowspan="3">Spans three rows</td>
<tdrowspan="3">Also spans three</td>
</tr>
<tr>
</tr>
<tr>
</tr>
</tbody>
</table>
Rows 2 and 3 have no cells beginning on them — all cells originate from row 1. Even though cells pass through those rows via rowspan, the validator still requires at least one cell to begin on each row.
Corrected rowspan with cells on every row (correct)
<table>
<tbody>
<tr>
<tdrowspan="3">Spans three rows</td>
<td>Row 1 detail</td>
</tr>
<tr>
<td>Row 2 detail</td>
</tr>
<tr>
<td>Row 3 detail</td>
</tr>
</tbody>
</table>
Each row now has at least one <td> that begins on it, satisfying the requirement.
Using CSS instead of empty rows for spacing (correct)
<table>
<tbody>
<tr>
<td>First item</td>
</tr>
<trstyle="height:1.5em;">
<td>Second item with extra space above</td>
</tr>
</tbody>
</table>
Instead of inserting an empty row for spacing, apply CSS to the row or cells that need additional space.
Every valid HTML document follows a strict structure: a <!DOCTYPE html> declaration, followed by an <html> element containing exactly two children — <head> and <body>. All visible content and its associated markup must live inside <body>. Once the browser encounters the </body> closing tag, it expects nothing else except the closing </html> tag.
When an end tag appears after </body> has been closed, the browser's parser enters an error-recovery mode. While most browsers will attempt to handle this gracefully, the behavior is not guaranteed to be consistent. This can lead to unexpected DOM structures, which may cause issues with JavaScript that relies on the DOM tree, break CSS styling, or create accessibility problems for screen readers that depend on proper document structure.
This issue commonly arises from:
- Extra or duplicate closing tags — for example, an extra
</div>that doesn't match any open element inside<body>. - Accidentally placing elements after
</body>— such as moving a<script>or</div>outside the body during editing. - Copy-paste errors — fragments of markup pasted in the wrong location.
- Template or CMS issues — server-side templates that inject closing tags in the wrong order.
To fix this, review your document structure carefully. Make sure every opening tag inside <body> has a matching closing tag also inside <body>. Ensure nothing appears between </body> and </html> except whitespace.
Examples
Incorrect — end tag after body is closed
<!DOCTYPE html>
<htmllang="en">
<head>
<title>My Page</title>
</head>
<body>
<div>
<p>Hello world</p>
</div>
</body>
</div>
</html>
Here, an extra </div> sits between </body> and </html>. This triggers the validation error because the body has already been closed when the parser encounters </div>.
Incorrect — script tag placed after body
<!DOCTYPE html>
<htmllang="en">
<head>
<title>My Page</title>
</head>
<body>
<p>Hello world</p>
</body>
<scriptsrc="app.js"></script>
</html>
While browsers will typically still execute the script, placing it after </body> is invalid and produces this warning.
Correct — all content and tags inside body
<!DOCTYPE html>
<htmllang="en">
<head>
<title>My Page</title>
</head>
<body>
<div>
<p>Hello world</p>
</div>
<scriptsrc="app.js"></script>
</body>
</html>
All elements, including <script> tags, are properly nested inside the <body> element. The only thing that follows </body> is </html>.
Debugging tip
If your document is large and you're having trouble finding the stray tag, try these approaches:
- Count your tags — make sure every opening tag (like
<div>) has exactly one matching</div>inside<body>. - Use an editor with bracket matching — most code editors can highlight matching open/close tags, making orphaned tags easy to spot.
- Check the validator's line number — the W3C validator reports the line where the stray end tag was found, which points you directly to the problem.
- Indent properly — consistent indentation makes structural problems visually obvious.
The HTML parser reads element attributes by looking for a name, then an =, then a value. When it encounters = in a spot where it expects a name to start, it means something has gone wrong with the attribute syntax. This can happen in several ways: an attribute name is missing entirely before the =, there's a double == instead of a single =, a space was accidentally inserted in the middle of an attribute name, or a previous attribute's value was left unquoted causing the parser to lose track of where one attribute ends and the next begins.
This is a problem because browsers may silently discard the malformed attribute, apply it incorrectly, or even misinterpret surrounding attributes. This leads to unpredictable behavior across different browsers. Screen readers and other assistive technologies may also fail to interpret the element correctly, creating accessibility issues.
How to Fix It
- Check for missing attribute names — Make sure every
=sign is preceded by a valid attribute name likeclass,id,src, etc. - Check for double equals signs — Replace any
==with a single=. - Check for spaces in attribute names — Attribute names cannot contain spaces. Remove accidental spaces within names.
- Check for unquoted attribute values — Always quote attribute values with double quotes. An unquoted value containing special characters can cause the parser to misread subsequent attributes.
- Check for stray
=characters — Look for leftover=signs from copy-paste errors or incomplete edits.
Examples
Missing attribute name before =
An = appears with no attribute name before it:
<!-- ❌ Bad: no attribute name before = -->
<img="photo.jpg" alt="A photo">
<!-- ✅ Fixed: added the src attribute name -->
<imgsrc="photo.jpg"alt="A photo">
Double equals sign
A typo creates == instead of =:
<!-- ❌ Bad: double equals sign -->
<ahref=="https://example.com">Link</a>
<!-- ✅ Fixed: single equals sign -->
<ahref="https://example.com">Link</a>
Space inside an attribute name
A space splits what should be one attribute name into two tokens, leaving an orphaned =:
<!-- ❌ Bad: space in "data-value" splits the attribute -->
<divdata-value="10">Content</div>
<!-- ✅ Fixed: no space in the attribute name -->
<divdata-value="10">Content</div>
Unquoted attribute value causing a cascade
When a value isn't quoted, the parser can misinterpret where it ends, causing the next attribute's = to appear in an unexpected position:
<!-- ❌ Bad: unquoted class value with space causes parsing issues -->
<pclass=myclassid="intro">Text</p>
<!-- ✅ Fixed: properly quoted attribute value -->
<pclass="my-class"id="intro">Text</p>
Stray = from an incomplete edit
A leftover = from a deleted or partially edited attribute:
<!-- ❌ Bad: stray = left over after removing an attribute -->
<divclass="container"=>Content</div>
<!-- ✅ Fixed: removed the stray = -->
<divclass="container">Content</div>
When the HTML parser reads a tag, it expects a specific sequence: the tag name, then optional attributes (each with a name, optionally followed by = and a value), and finally a closing >. If the parser encounters a < character in a position where it's looking for an attribute name, it means something has gone structurally wrong. The parser interprets the < as the beginning of a new tag, but since it's still inside the current tag's definition, it raises this error.
This issue commonly arises from three scenarios:
- A stray
<character inside a tag — perhaps from a typo or a copy-paste error. - A missing
>on a previous tag — causing the parser to treat the next tag's<as though it's still part of the previous tag's attributes. - Incorrectly nested or overlapping tags — where one element is accidentally placed inside another element's opening tag.
This matters because browsers handle malformed HTML unpredictably. One browser might silently ignore the stray character, while another might drop the entire element or render content incorrectly. Fixing these structural errors ensures consistent rendering across all browsers and improves accessibility, since screen readers and other assistive technologies rely on well-formed markup to interpret page structure.
Examples
Stray < character inside a tag
A common typo where an extra < appears before the closing of a self-closing tag or between attributes:
<!-- ❌ Stray "<" inside the img tag -->
<imgsrc="photo.jpg"alt="smiling cat"</>
Remove the stray < character:
<!-- ✅ Fixed: no extra "<" -->
<imgsrc="photo.jpg"alt="smiling cat"/>
Missing > on a preceding tag
When a tag is missing its closing >, the parser continues reading the next tag as though it were part of the first tag's attributes:
<!-- ❌ The opening <div> is missing its closing ">" -->
<divclass="wrapper"
<p>Hello, world!</p>
</div>
The parser sees <p> while still inside the <div> tag, triggering the error. Add the missing >:
<!-- ✅ Fixed: <div> is properly closed -->
<divclass="wrapper">
<p>Hello, world!</p>
</div>
Missing > on a tag with multiple attributes
This often happens with tags that have many attributes, making it easy to miss the closing >:
<!-- ❌ The <a> tag is missing its closing ">" -->
<ahref="/about"class="nav-link"id="about-link"
<span>About Us</span>
</a>
<!-- ✅ Fixed: closing ">" added to the <a> tag -->
<ahref="/about"class="nav-link"id="about-link">
<span>About Us</span>
</a>
Accidental angle bracket in an attribute value
If an attribute value contains a < that isn't properly quoted, the parser may misinterpret it:
<!-- ❌ Unquoted or broken attribute value with "<" -->
<divtitle=5<10>
<p>Content</p>
</div>
Ensure attribute values containing special characters are properly quoted and use HTML entities where needed:
<!-- ✅ Fixed: value is quoted and uses an entity for "<" -->
<divtitle="5<10">
<p>Content</p>
</div>
How to debug this error
When you see this error, follow these steps:
- Go to the line number reported by the validator.
- Look at the tag on that line — check if it has a stray
<character. - If the tag looks fine, check the tag immediately before it — a missing
>on the previous tag is often the real culprit. - Verify all attribute values are properly quoted — unquoted values containing
<can trigger this error. - Use a code editor with syntax highlighting — mismatched or broken tags are usually easy to spot when the syntax colors look wrong.
XML processing instructions are a feature of XML, not HTML. They begin with <? and end with ?>, and are used in XML documents to carry instructions for applications processing the document. The most common example is the XML declaration: <?xml version="1.0" encoding="UTF-8"?>. While these are perfectly valid in XML, the HTML parser does not recognize them. When the parser encounters <?, it doesn't know how to handle it and treats it as a bogus comment, which leads to unexpected behavior and this validation error.
This matters for several reasons. First, standards compliance — HTML5 has a clearly defined parsing algorithm, and processing instructions are not part of it. Second, browser behavior becomes unpredictable — different browsers may handle the unexpected <? content differently, potentially exposing raw code or breaking your layout. Third, if server-side code like PHP leaks into the output, it can expose sensitive logic or configuration details to end users.
There are three common causes of this error:
1. Inlining SVG files with their XML declaration. When you copy the contents of an .svg file and paste it directly into HTML, the file often starts with an XML declaration and possibly a <?xml-stylesheet?> processing instruction. These must be removed — only the <svg> element and its children should be included.
2. Unprocessed server-side code. Languages like PHP use <?php ... ?> (or the short tag <? ... ?>) syntax. If the server isn't configured to process PHP files, or if a .html file contains PHP code without being routed through the PHP interpreter, the raw <?php tags end up in the HTML sent to the browser.
3. Copy-pasting XML content. Other XML-based formats (like RSS feeds, XHTML fragments, or MathML with XML declarations) may include processing instructions that aren't valid in HTML.
How to Fix It
- For inline SVGs: Open the SVG file in a text editor and copy only the
<svg>...</svg>element. Remove the<?xml ...?>declaration and any other processing instructions that precede the<svg>tag. - For PHP or other server-side code: Ensure your server is properly configured to process the files. Check that the file extension matches what the server expects (e.g.,
.phpfor PHP files). Verify that the server-side language is installed and running. - For other XML content: Strip out any
<?...?>processing instructions before embedding the content in HTML.
Examples
❌ Inline SVG with XML declaration
<body>
<?xml version="1.0" encoding="UTF-8"?>
<svgxmlns="http://www.w3.org/2000/svg"viewBox="0 0 100 100">
<circlecx="50"cy="50"r="40"fill="blue"/>
</svg>
</body>
The <?xml version="1.0" encoding="UTF-8"?> line triggers the error.
✅ Inline SVG without XML declaration
<body>
<svgxmlns="http://www.w3.org/2000/svg"viewBox="0 0 100 100">
<circlecx="50"cy="50"r="40"fill="blue"/>
</svg>
</body>
❌ Unprocessed PHP code in HTML output
<body>
<h1>Welcome</h1>
<p><?php echo "Hello, World!"; ?></p>
</body>
If the server doesn't process the PHP, the raw <?php ... ?> ends up in the HTML and triggers this error.
✅ Properly processed output (or static HTML equivalent)
<body>
<h1>Welcome</h1>
<p>Hello, World!</p>
</body>
❌ SVG with an XML stylesheet processing instruction
<body>
<?xml-stylesheet type="text/css" href="style.css"?>
<svgxmlns="http://www.w3.org/2000/svg"viewBox="0 0 50 50">
<rectwidth="50"height="50"fill="red"/>
</svg>
</body>
✅ SVG without the processing instruction
<body>
<svgxmlns="http://www.w3.org/2000/svg"viewBox="0 0 50 50">
<rectwidth="50"height="50"fill="red"/>
</svg>
</body>
In HTML, use standard <link> or <style> elements in the <head> for stylesheets instead of XML processing instructions.
The < and > characters have special meaning in HTML — they signal the start and end of tags. When the parser encounters </>, it sees what looks like a closing tag with no element name, which is invalid in HTML. This sequence can appear in your markup for two main reasons:
- Unescaped text content: You're trying to display the literal characters
</>as visible text on the page (common in tutorials, documentation, or code snippets), but the browser interprets them as markup rather than content. - Mistyped end tag: You intended to write a proper closing tag like
</p>or</div>but accidentally omitted the element name, leaving just</>.
This matters because browsers may silently discard the malformed tag or interpret it in unexpected ways, leading to broken layouts or missing content. Screen readers and other assistive technologies may also struggle with the resulting DOM structure. Properly escaping special characters and writing well-formed tags ensures consistent rendering across all browsers and devices.
To fix this, determine which scenario applies. If you want to display the literal text </>, replace < with < and > with >. If you meant to close an element, add the correct element name between </ and >.
Examples
Unescaped angle brackets in text content
This triggers the error because the parser sees </> as an invalid closing tag:
<!-- ❌ Bad: raw </> in text content -->
<p>In JSX, self-closing tags use the </> syntax.</p>
Escape the angle brackets using HTML character entities:
<!-- ✅ Good: properly escaped characters -->
<p>In JSX, self-closing tags use the </> syntax.</p>
Mistyped closing tag with missing element name
This triggers the error because the closing tag has no name:
<!-- ❌ Bad: empty closing tag -->
<divclass="container">
<p>Some content here.</p>
</>
Add the correct element name to the closing tag:
<!-- ✅ Good: proper closing tag -->
<divclass="container">
<p>Some content here.</p>
</div>
Displaying code snippets with angle brackets
When writing about HTML or XML in your page content, all angle brackets in text must be escaped:
<!-- ❌ Bad: unescaped tags in text -->
<p>Use <strong> to make text bold and </strong> to close it.</p>
<!-- ✅ Good: escaped tags in text -->
<p>Use <strong> to make text bold and </strong> to close it.</p>
Using the <code> element for inline code
Even inside <code> elements, angle brackets must still be escaped — the <code> element only changes visual presentation, it does not prevent HTML parsing:
<!-- ❌ Bad: unescaped inside <code> -->
<p>A React fragment looks like <code><></code> and <code></></code>.</p>
<!-- ✅ Good: escaped inside <code> -->
<p>A React fragment looks like <code><></code> and <code></></code>.</p>
Using <pre> blocks for larger code examples
The same escaping rules apply within <pre> elements:
<!-- ✅ Good: escaped characters inside pre -->
<pre><code><div>
<p>Hello, world!</p>
</div></code></pre>
If you frequently need to display code and find manual escaping tedious, consider using a JavaScript-based syntax highlighting library that handles escaping automatically, or use a build tool or templating engine that escapes HTML entities for you.
U+0000, commonly called the "null character" or NUL, is a non-printable control character. It's invisible in most text editors and browsers, so you won't see it just by looking at your code. However, the HTML parsing algorithm defined by the WHATWG HTML Living Standard treats U+0000 as a parse error in virtually every context — whether it appears in text content, attribute values, comments, or anywhere else in the document.
When a browser encounters a null character, it typically either ignores it or replaces it with the Unicode replacement character (U+FFFD, �). This means the character serves no useful purpose and can only cause problems.
Why Does This Happen?
Null characters usually sneak into HTML files through one of these common scenarios:
- Copy and paste from applications like word processors, PDFs, or terminal output that embed hidden control characters.
- File encoding corruption, such as when a file is converted between encodings incorrectly or transferred in binary mode.
- Editor or build tool bugs that inadvertently insert null bytes into output files.
- Database content that contains null characters which then gets rendered into HTML templates.
- Concatenation of binary data with text during a build process.
Why It Matters
- Standards compliance: The HTML specification explicitly forbids U+0000. Its presence makes your document non-conforming.
- Unpredictable rendering: Different browsers may handle null characters differently — some ignore them, others replace them with
�, which can produce visible artifacts in your content. - Accessibility: Screen readers and assistive technologies may behave unexpectedly when encountering null characters, potentially skipping content or producing garbled output.
- Data integrity: The presence of null characters is often a symptom of a deeper problem, such as encoding corruption, that could affect other parts of your content.
How to Fix It
Reveal hidden characters in your text editor. Most code editors have this option:
- In VS Code, use the setting
"editor.renderControlCharacters": true. - In Sublime Text, look for non-printable characters via Find & Replace with regex enabled.
- In Vim, null characters typically display as
^@.
- In VS Code, use the setting
Search and remove null characters. Use your editor's find-and-replace with regex support to search for
\x00or\0and replace with nothing (an empty string).Use command-line tools for a quick fix:
- On Linux/macOS:
tr -d '\0' < input.html > output.html - With
sed:sed -i 's/\x0//g' file.html
- On Linux/macOS:
Check your build pipeline. If the null characters reappear after cleaning, the issue likely originates upstream — in a database, a template engine, or a build step that concatenates files.
Re-validate your HTML after cleaning to confirm the issue is resolved.
Examples
Incorrect — null character in text content
In the example below, \0 represents the invisible U+0000 character for illustration purposes:
<p>Welcome to our site!\0 We're glad you're here.</p>
This triggers the "Saw U+0000 in stream" error.
Correct — null character removed
<p>Welcome to our site! We're glad you're here.</p>
Incorrect — null character inside an attribute value
<ahref="/about\0us">About Us</a>
Correct — null character removed from attribute
<ahref="/about-us">About Us</a>
Incorrect — null character in a comment
<!-- This comment has a null \0 character -->
Correct — clean comment
<!-- This comment has no null character -->
Null characters are forbidden in all contexts, so no matter where they appear — in elements, attributes, comments, or even the DOCTYPE — they must be removed. If you consistently see this error after saving files, investigate your editor's encoding settings and ensure files are saved as UTF-8 without any binary artifacts.
The HTML parser reads element tags by looking for attribute names, an = sign, and then a quoted value. When the parser finds a " character in a position where it expects an attribute name to start, it means something has gone wrong with the syntax. The validator flags this as a parse error because the browser has to guess what you intended, which can lead to attributes being silently dropped, values being misassigned, or unexpected rendering behavior.
This error most commonly occurs due to one of these causes:
- Missing
=sign between an attribute name and its value (e.g.,class"main"instead ofclass="main"). - Extra closing quote that creates a stray
"after a valid attribute (e.g.,class="main""with a doubled quote). - Stray quote from copy-paste errors or typos that leave orphan
"characters floating in the tag. - Accidentally deleted
=during editing, breaking an otherwise valid attribute.
Beyond being invalid HTML, this problem can cause real functional issues. A missing = sign may cause the browser to interpret the attribute value as a separate (unknown) attribute, effectively losing the intended value. An extra quote can cause the parser to misread subsequent attributes, potentially breaking event handlers, links, or styling. These issues can also harm accessibility, as assistive technologies rely on properly parsed attributes to convey meaning.
Examples
Missing equals sign
The = between the attribute name and value is absent, so the parser sees "container" where it expects a new attribute name.
<!-- ❌ Bad: missing = before the value -->
<divclass"container">Content</div>
<!-- ✅ Fixed: = added between name and value -->
<divclass="container">Content</div>
Extra closing quotation mark
A doubled " at the end of an attribute value leaves a stray quote that the parser doesn't expect.
<!-- ❌ Bad: extra " after the attribute value -->
<ahref="https://example.com"">Visit</a>
<!-- ✅ Fixed: single closing quote -->
<ahref="https://example.com">Visit</a>
Multiple attributes with a stray quote
When multiple attributes are present, a stray quote can also corrupt the parsing of subsequent attributes.
<!-- ❌ Bad: extra " after id value bleeds into the next attribute -->
<inputid="email"" type="text" name="email">
<!-- ✅ Fixed: clean quotes on every attribute -->
<inputid="email"type="text"name="email">
Missing equals sign on a later attribute
The error doesn't always occur on the first attribute — it can appear on any attribute in the tag.
<!-- ❌ Bad: missing = on the style attribute -->
<pclass="intro"style"color: red;">Hello</p>
<!-- ✅ Fixed: = added before style value -->
<pclass="intro"style="color: red;">Hello</p>
How to fix it
- Locate the line reported by the validator and look at the element's attributes.
- Check every attribute follows the
name="value"pattern with no missing=signs. - Count your quotes — every opening
"should have exactly one matching closing"for each attribute value. - Watch for smart quotes — curly quotes (
"") copied from word processors are not valid HTML attribute delimiters. Replace them with straight quotes ("). - Use a code editor with syntax highlighting — mismatched or extra quotes are usually easy to spot when your editor color-codes attribute values.
Nested comments are not allowed in HTML. When you place an opening comment tag <!-- within an existing comment, it causes a parsing error.
To fix this issue, you should avoid nesting comments. If you need to include multiple comments in your HTML code, you can separate them or consider alternative ways to convey the information without nested comments.
Here's an example of a nested comments, which is not allowed in HTML:
<!-- This is a valid comment -->
<!-- This <!-- is not allowed --> nested comment -->
To fix the nested comment issue, you can rewrite the comments like this:
<!-- This is a valid comment -->
<!-- Avoid nesting comments and provide comments separately -->
<!-- Another comment here -->
The <section> element defines a standalone thematic grouping within a document — think chapters, tabbed panels, or distinct parts of a page like "About Us" or "Contact." According to the HTML specification, each <section> should generally include a heading that identifies its content. When a <section> lacks a heading, the validator raises this warning because the element isn't being used as intended.
This matters for several reasons:
Accessibility. Screen readers and other assistive technologies use headings to build an outline of the page. When a user navigates by sections, the heading is what identifies each one. A <section> without a heading appears as an "untitled section" in the document outline, which makes it difficult or impossible for assistive technology users to understand the page structure or jump to the content they need.
Semantics. The whole purpose of <section> is to group related content under a common theme, and that theme should be labeled with a heading. If your content doesn't need a heading, it may not be a true "section" in the semantic sense, and a <div> (which carries no semantic meaning) might be a better fit.
Document outline. Browsers and tools that generate document outlines rely on headings within sectioning elements. Missing headings create gaps in the outline, reducing the usefulness of the page structure.
How to fix it
You have three main options:
Add a heading — Place an
<h2>–<h6>element at the beginning of the<section>. This is the preferred solution when the section genuinely represents a thematic block of content.Use
aria-labeloraria-labelledby— If a visible heading doesn't suit your design, you can label the section for assistive technologies using an ARIA attribute. This resolves the accessibility concern, though the validator may still show the warning.Switch to
<div>— If the content doesn't represent a meaningful, identifiable section of the document, replace<section>with a<div>. There's no expectation for a<div>to have a heading.
Examples
❌ Section without a heading
This triggers the warning because neither <section> has a heading:
<h1>All about guitars</h1>
<section>
<p>Acoustic, electric, classical... we have them all!</p>
</section>
<section>
<p>Analog, digital, portable...</p>
</section>
✅ Fixed: Adding headings to each section
<h1>All about guitars</h1>
<section>
<h2>Guitar types</h2>
<p>Acoustic, electric, classical... we have them all!</p>
</section>
<section>
<h2>Amplifiers</h2>
<p>Analog, digital, portable...</p>
</section>
✅ Fixed: Using aria-label when a visible heading isn't appropriate
<sectionaria-label="Guitar types">
<p>Acoustic, electric, classical... we have them all!</p>
</section>
✅ Fixed: Using aria-labelledby to reference an existing element
<sectionaria-labelledby="guitar-heading">
<spanid="guitar-heading"class="visually-hidden">Guitar types</span>
<p>Acoustic, electric, classical... we have them all!</p>
</section>
✅ Fixed: Replacing with <div> when no section semantics are needed
If you're only using the element as a styling wrapper and the content doesn't form a distinct thematic group, use a <div> instead:
<h1>All about guitars</h1>
<divclass="content-block">
<p>Acoustic, electric, classical... we have them all!</p>
</div>
<divclass="content-block">
<p>Analog, digital, portable...</p>
</div>
As a general rule, reach for <section> when your content has a clear topic that deserves a heading, and use <div> when you need a generic container for layout or styling purposes.
In HTML, elements fall into two categories regarding their content model: void elements and non-void elements. Void elements like <img>, <br>, <hr>, <input>, <meta>, and <link> cannot have any content and never need a closing tag. Non-void elements like <div>, <p>, <span>, <select>, <textarea>, and <script> can (or must) contain content and require both an opening tag and a closing tag.
When you write <div /> or <select />, you might expect the browser to treat it as a complete, self-contained element — similar to how XML or XHTML would interpret it. However, the HTML parser does not work this way. According to the WHATWG HTML Living Standard, the /> on a non-void element is simply ignored. The browser treats <div /> as just <div> — an opening tag without a corresponding closing tag. This can lead to serious structural problems in your document, as the browser will keep looking for content and a closing tag, potentially swallowing subsequent elements as children.
This issue commonly arises when developers are accustomed to frameworks like React (JSX), where <Component /> syntax is standard, or when coming from an XML/XHTML background where any element can be self-closed. It also appears when developers mistakenly believe that an element with no intended content can be self-closed in HTML.
The consequences can be significant. The browser's DOM tree may end up drastically different from what you intended, causing layout problems, broken functionality, and accessibility issues. For example, a self-closed <script /> tag won't behave as expected — the browser will treat everything after it as the script's content until it finds a </script> closing tag somewhere else in the document, effectively hiding your visible page content.
How to fix it
- Remove the trailing slash from the non-void element's opening tag.
- Add a proper closing tag immediately after the opening tag if the element should be empty, or after its content.
The complete list of void elements in HTML is: <area>, <base>, <br>, <col>, <embed>, <hr>, <img>, <input>, <link>, <meta>, <source>, <track>, and <wbr>. Only these elements may use self-closing syntax (and even for these, the / is optional in HTML).
Examples
Incorrect: Self-closing syntax on non-void elements
<div/>
<select/>
<optionvalue="1">one</option>
</select>
<span/>
<textarea/>
<scriptsrc="app.js"/>
Each of these will trigger the warning. The validator ignores the slash and treats them as opening tags, meaning the browser expects content and a closing tag to follow.
Correct: Using proper closing tags
<div></div>
<select>
<optionvalue="1">one</option>
</select>
<span></span>
<textarea></textarea>
<scriptsrc="app.js"></script>
Correct: Self-closing syntax on void elements
Void elements can optionally use the trailing slash. Both forms below are valid:
<!-- With optional trailing slash -->
<imgsrc="photo.jpg"alt="A photo"/>
<br/>
<inputtype="text"name="username"/>
<!-- Without trailing slash (equally valid) -->
<imgsrc="photo.jpg"alt="A photo">
<br>
<inputtype="text"name="username">
A subtle trap: <script />
This example demonstrates one of the most dangerous cases of this issue:
<!-- Incorrect: browser treats everything after this as script content -->
<scriptsrc="app.js"/>
<p>This paragraph will not be rendered — it's consumed as script text.</p>
<!-- Correct -->
<scriptsrc="app.js"></script>
<p>This paragraph renders normally.</p>
The browser interprets the incorrect version as an opening <script> tag and reads all subsequent markup as script content until it encounters </script>, effectively hiding your page content and likely producing JavaScript errors.
The HTML specification defines <a> elements as having a transparent content model, but with specific exceptions — most notably, they must not contain interactive content, which includes other <a> elements. This means you can never legally nest one link inside another, regardless of how deeply nested or how the markup is structured.
This error most commonly occurs for one of two reasons:
- A missing closing tag: You forgot to close a previous
<a>element with</a>, so the browser (and the validator) considers the next<a>tag to be nested inside the still-open one. - Intentionally nested links: You tried to place a link inside another link, perhaps to create a card component where both the card and an inner element are clickable.
Why this matters
- Unpredictable browser behavior: When browsers encounter nested
<a>elements, they attempt to fix the invalid markup using error-recovery algorithms, but different browsers may handle it differently. This can lead to broken links, missing content, or unexpected DOM structures. - Accessibility issues: Screen readers and other assistive technologies rely on a well-formed DOM. Nested links create confusing navigation — a user tabbing through links may encounter unexpected behavior or miss content entirely.
- Standards compliance: The WHATWG HTML specification explicitly states that
<a>elements must not have interactive content descendants, including other<a>elements.
Examples
Missing closing tag (most common cause)
The most frequent trigger is simply forgetting to close an <a> tag. The validator sees the second <a> as being inside the first:
<!-- ❌ Bad: first <a> is never closed -->
<nav>
<ahref="one.html">Page 1
<ahref="two.html">Page 2</a>
</nav>
Add the missing </a> to fix it:
<!-- ✅ Good: both <a> elements are properly closed -->
<nav>
<ahref="one.html">Page 1</a>
<ahref="two.html">Page 2</a>
</nav>
Intentionally nested links
Sometimes developers try to nest links when building clickable card components:
<!-- ❌ Bad: <a> nested inside another <a> -->
<ahref="/article"class="card">
<h2>Article Title</h2>
<p>A short description of the article.</p>
<ahref="/author">Author Name</a>
</a>
There are several valid approaches to fix this. One common pattern is to make the card a non-link container and use CSS to stretch the primary link over the entire card:
<!-- ✅ Good: no nested links, card is still fully clickable -->
<divclass="card">
<h2><ahref="/article"class="card-link">Article Title</a></h2>
<p>A short description of the article.</p>
<ahref="/author">Author Name</a>
</div>
.card{
position: relative;
}
.card-link::after{
content:"";
position: absolute;
inset:0;
}
.carda:not(.card-link){
position: relative;
z-index:1;
}
This technique uses a pseudo-element to expand the primary link's clickable area over the entire card, while the secondary link (Author Name) sits above it via z-index, remaining independently clickable.
Links wrapping block-level content
In HTML5, <a> elements can wrap block-level elements like <div> and <h2>, which is perfectly valid. Just make sure you don't accidentally nest another <a> inside:
<!-- ✅ Good: <a> wrapping block content with no nested links -->
<ahref="/article">
<divclass="card">
<h2>Article Title</h2>
<p>A short description of the article.</p>
</div>
</a>
Quick checklist
- Search your HTML for every
<atag and verify it has a corresponding</a>. - If you're generating HTML dynamically (with a CMS, templating engine, or JavaScript), check that your template logic doesn't produce unclosed or nested anchors.
- If you need multiple clickable areas within a single component, use the CSS pseudo-element overlay technique shown above instead of nesting links.
Validate at scale.
Ship accessible websites, faster.
Automated HTML & accessibility validation for large sites. Check thousands of pages against WCAG guidelines and W3C standards in minutes, not days.
Pro Trial
Full Pro access. Cancel anytime.
Start Pro Trial →Join teams across 40+ countries