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 HTML specification requires that if the name attribute is used on a <form> element, its value must be a non-empty string. An empty name="" attribute serves no practical purpose — it doesn’t register the form in the document.forms named collection, and it can’t be used as a valid reference in scripts. The W3C validator flags this as an error because it violates the content model defined in the WHATWG HTML Living Standard.
The name attribute on a form is primarily used to access the form programmatically through document.forms["formName"]. When the value is empty, this lookup mechanism doesn’t work, so the attribute becomes meaningless. This is different from the id attribute, which also identifies elements but participates in fragment navigation and CSS targeting. The name attribute on <form> is specifically for the legacy document.forms named getter interface.
It’s worth noting that the name attribute value on a form must not equal an empty string, and it should be unique among the form elements in the forms collection. While duplicate names won’t cause a validation error, they can lead to unexpected behavior when accessing forms by name in JavaScript.
How to fix
You have two options:
- Remove the attribute entirely. If you’re not referencing the form by name in JavaScript, the name attribute is unnecessary. You can use id instead for CSS or JavaScript targeting.
- Provide a meaningful, non-empty value. If you need to reference the form through document.forms, give it a descriptive name that reflects its purpose.
Examples
❌ Invalid: empty name attribute
<form name="">
<label for="email">Email</label>
<input type="email" id="email" name="email">
<button type="submit">Subscribe</button>
</form>
This triggers the validator error because the name attribute is present but has an empty string value.
✅ Fixed: attribute removed
<form>
<label for="email">Email</label>
<input type="email" id="email" name="email">
<button type="submit">Subscribe</button>
</form>
If you don’t need to reference the form by name, simply remove the attribute.
✅ Fixed: meaningful name provided
<form name="subscriptionForm">
<label for="email">Email</label>
<input type="email" id="email" name="email">
<button type="submit">Subscribe</button>
</form>
With a non-empty name, you can now access the form in JavaScript using document.forms["subscriptionForm"] or document.forms.subscriptionForm.
✅ Alternative: using id instead
<form id="subscription-form">
<label for="email">Email</label>
<input type="email" id="email" name="email">
<button type="submit">Subscribe</button>
</form>
In modern development, using id with document.getElementById() or document.querySelector() is often preferred over the name attribute for form identification. The name attribute on <form> is a legacy feature that remains valid but isn’t required for most use cases.
The name attribute on an <input> element identifies the form control’s data when the form is submitted. It acts as the key in the key-value pair sent to the server (e.g., email=user@example.com). When you set name="", the attribute is present but contains an empty string, which the HTML specification considers an invalid value. An empty name means the input’s data will be excluded from the form’s submission payload in most browsers, making it functionally useless for form processing.
This issue matters for several reasons:
- Form functionality: Inputs with empty names are typically omitted from form data, so the server never receives the user’s input.
- Standards compliance: The HTML specification requires that if the name attribute is present, its value must not be empty.
- JavaScript references: An empty name makes it difficult to reference the element using methods like document.getElementsByName() or FormData.
- Accessibility: Screen readers and assistive technologies may use the name attribute to help identify form controls, and an empty value provides no useful information.
Note that the name attribute is not technically required on every <input> element — it’s perfectly valid to omit it entirely. For example, inputs used purely for client-side JavaScript interactions without form submission don’t need a name. The error specifically arises when the attribute is present but set to an empty string.
To fix the issue, either assign a meaningful name that describes the data the input collects, or remove the name attribute altogether if the input isn’t part of a form submission.
Examples
❌ Empty name attribute triggers the error
<form action="/submit" method="post">
<label for="email">Email:</label>
<input type="email" id="email" name="">
<button type="submit">Submit</button>
</form>
✅ Providing a meaningful name value
<form action="/submit" method="post">
<label for="email">Email:</label>
<input type="email" id="email" name="email">
<button type="submit">Submit</button>
</form>
✅ Removing name when the input isn’t submitted
If the input is only used for client-side interaction and doesn’t need to be part of form data, simply omit the attribute:
<label for="search">Filter results:</label>
<input type="text" id="search">
❌ Multiple inputs with empty names
This pattern sometimes appears when inputs are generated dynamically with placeholder attributes:
<form action="/register" method="post">
<input type="text" name="">
<input type="password" name="">
<button type="submit">Register</button>
</form>
✅ Each input gets a descriptive name
<form action="/register" method="post">
<label for="username">Username:</label>
<input type="text" id="username" name="username">
<label for="password">Password:</label>
<input type="password" id="password" name="password">
<button type="submit">Register</button>
</form>
The poster attribute tells the browser which image to display as a preview frame before the user plays the video. According to the WHATWG HTML Living Standard, the poster attribute’s value must be a valid non-empty URL potentially surrounded by spaces. When you include poster="", the attribute is present but its value is an empty string, which is not a valid URL — triggering the W3C validator error: Bad value “” for attribute “poster” on element “video”: Must be non-empty.
This issue commonly occurs in a few scenarios:
- Template or CMS output where the poster URL is dynamically generated but the variable resolves to an empty string (e.g., poster="<?= $posterUrl ?>" when $posterUrl is empty).
- JavaScript frameworks that bind a value to the poster attribute, but the bound variable is null, undefined, or an empty string.
- Manual editing where a developer adds the attribute as a placeholder intending to fill it in later.
Why This Matters
While most browsers will gracefully handle an empty poster attribute by simply not displaying a poster image, there are good reasons to fix this:
- Standards compliance: An empty poster value violates the HTML specification. Valid markup ensures predictable behavior across all browsers and devices.
- Unnecessary network requests: Some browsers may attempt to resolve the empty string as a relative URL, potentially triggering a failed HTTP request to the current page’s URL. This wastes bandwidth and clutters developer tools and server logs with spurious errors.
- Accessibility: Screen readers and assistive technologies may interpret the empty attribute inconsistently, leading to confusing announcements for users.
How to Fix It
You have two straightforward options:
- Provide a valid image URL — If you want a poster frame, set the value to a real image path.
- Remove the attribute entirely — If you don’t need a poster image, simply omit poster. The browser will either show nothing or display the first frame of the video once enough data has loaded, depending on the preload attribute setting.
If the empty value comes from dynamic code, add a conditional check so the poster attribute is only rendered when a URL is actually available.
Examples
❌ Invalid: Empty poster attribute
<video controls poster="">
<source src="movie.mp4" type="video/mp4">
Your browser does not support the video tag.
</video>
This triggers the validation error because poster is present but has no value.
✅ Fixed: Poster attribute with a valid URL
<video controls poster="thumbnail.jpg">
<source src="movie.mp4" type="video/mp4">
Your browser does not support the video tag.
</video>
The poster attribute now points to a valid image file that the browser will display before playback begins.
✅ Fixed: Poster attribute removed entirely
<video controls>
<source src="movie.mp4" type="video/mp4">
Your browser does not support the video tag.
</video>
When no poster image is needed, omitting the attribute is the cleanest solution.
✅ Fixed: Conditional rendering in a template
If you’re using a templating language or framework, conditionally include the attribute only when a value exists. Here’s a conceptual example using a server-side template:
<!-- Pseudo-template syntax: only render poster when posterUrl is not empty -->
<video controls {{#if posterUrl}}poster="{{posterUrl}}"{{/if}}>
<source src="movie.mp4" type="video/mp4">
Your browser does not support the video tag.
</video>
This pattern prevents the empty poster="" from ever reaching the rendered HTML, keeping your output valid regardless of whether a poster URL is available.
The <textarea> element represents a multi-line plain-text editing control, commonly used for comments, feedback forms, and other free-form text input. The rows attribute specifies the number of visible text lines the textarea should display. According to the HTML specification, when the rows attribute is present, its value must be a valid positive integer — meaning a string of one or more digits representing a number greater than zero (e.g., 1, 5, 20). An empty string ("") does not meet this requirement.
This issue typically occurs when a template engine, CMS, or JavaScript framework dynamically sets the rows attribute but outputs an empty value instead of a number, or when a developer adds the attribute as a placeholder intending to fill it in later.
Why this matters
- Standards compliance: The HTML specification explicitly requires rows to be a positive integer when present. An empty string violates this rule.
- Unpredictable rendering: Browsers fall back to a default value (typically 2) when they encounter an invalid rows value, but this behavior isn’t guaranteed to be consistent across all browsers and versions.
- Maintainability: Invalid attributes can mask bugs in dynamic code that was supposed to provide a real value.
How to fix it
You have two options:
- Set a valid positive integer: Replace the empty string with a number like 4, 5, or whatever suits your design.
- Remove the attribute: If you don’t need to control the number of visible rows (or prefer to handle sizing with CSS), simply omit the rows attribute. The browser will use its default.
If the value is generated dynamically, ensure your code has a fallback so it never outputs an empty string. You can also control the textarea’s height using CSS (height or min-height) instead of relying on the rows attribute.
Examples
❌ Invalid: empty string for rows
<textarea name="comments" rows="" cols="25">
</textarea>
This triggers the error because "" is not a valid positive integer.
✅ Fixed: providing a valid positive integer
<textarea name="comments" rows="5" cols="25">
</textarea>
Setting rows="5" tells the browser to display five visible lines of text.
✅ Fixed: removing the attribute entirely
<textarea name="comments" cols="25">
</textarea>
When rows is omitted, the browser uses its default (typically 2 rows). This is perfectly valid.
✅ Alternative: using CSS for sizing
<textarea name="comments" style="height: 10em; width: 25ch;">
</textarea>
If you need precise control over the textarea’s dimensions, CSS properties like height, min-height, and width give you more flexibility than the rows and cols attributes. In this case, you can safely leave both attributes off.
The sizes attribute works together with the srcset attribute to help the browser choose the most appropriate image source before the page layout is calculated. It describes how wide the image will be displayed under various viewport conditions, allowing the browser to pick an optimally sized image from the candidates listed in srcset. According to the HTML specification, the value of sizes must be a valid source size list — a comma-separated set of media conditions paired with lengths, with a default length at the end. An empty string ("") does not satisfy this requirement and is therefore invalid.
When the browser encounters an empty sizes attribute, it cannot determine the intended display width of the image. This defeats the purpose of responsive images and may cause the browser to fall back to a default behavior (typically assuming 100vw), which could result in downloading an unnecessarily large image. Beyond the functional issue, an empty attribute signals a code quality problem — it often means a template is outputting the attribute even when no value has been configured.
Why this matters
- Standards compliance: The HTML specification explicitly requires sizes to be a non-empty, valid source size list when present. An empty value is a parse error.
- Performance: Without a proper sizes value, the browser cannot make an informed decision about which srcset candidate to download. This can lead to wasted bandwidth and slower page loads, especially on mobile devices.
- Code quality: Empty attributes clutter your markup and suggest missing configuration, making the code harder to maintain.
How to fix it
- Provide a valid sizes value that describes how wide the image will actually render at different viewport widths.
- Remove sizes entirely if you are not using width descriptors (w) in srcset. When srcset uses pixel density descriptors (1x, 2x), the sizes attribute is not needed.
- Remove both sizes and srcset if you don’t need responsive image selection at all.
Examples
❌ Empty sizes attribute
<img
src="photo.jpg"
srcset="photo-small.jpg 480w, photo-large.jpg 1200w"
sizes=""
alt="A mountain landscape">
The empty sizes="" triggers the validation error.
✅ Valid sizes with a single default value
If the image always takes up the full viewport width:
<img
src="photo.jpg"
srcset="photo-small.jpg 480w, photo-large.jpg 1200w"
sizes="100vw"
alt="A mountain landscape">
✅ Valid sizes with media conditions
If the image displays at different widths depending on the viewport:
<img
src="photo.jpg"
srcset="photo-small.jpg 480w, photo-medium.jpg 800w, photo-large.jpg 1200w"
sizes="(max-width: 600px) 100vw, (max-width: 1024px) 50vw, 33vw"
alt="A mountain landscape">
This tells the browser: use 100vw on viewports up to 600px, 50vw up to 1024px, and 33vw otherwise.
✅ Removing sizes when using density descriptors
When srcset uses density descriptors instead of width descriptors, sizes is not applicable and should be omitted:
<img
src="photo.jpg"
srcset="photo.jpg 1x, photo-2x.jpg 2x"
alt="A mountain landscape">
✅ Removing both attributes when not needed
If responsive image selection isn’t required, simply use a standard <img>:
<img src="photo.jpg" alt="A mountain landscape">
Common template fix
If your CMS or templating system conditionally outputs these attributes, ensure the sizes attribute is only rendered when it has a value. For example, in a template:
<!-- Before (always outputs sizes, even when empty) -->
<img src="photo.jpg" srcset="{{srcset}}" sizes="{{sizes}}" alt="{{alt}}">
<!-- After (only outputs sizes when it has a value) -->
<img src="photo.jpg" srcset="{{srcset}}" {{#if sizes}}sizes="{{sizes}}"{{/if}} alt="{{alt}}">
Square brackets ([ and ]) are reserved characters under RFC 3986, the standard that defines URI syntax. They are only permitted in the host component of a URL (specifically for IPv6 addresses) and are illegal elsewhere, including the query string. While most browsers are lenient and will load an <iframe> even when the src contains raw brackets, the W3C HTML Validator correctly flags this as an invalid URL value.
This pattern surfaces frequently when working with frameworks or APIs that use bracket notation to represent arrays or nested objects in query parameters — for example, filters[category]=news or items[0]=apple. These URLs work in a browser’s address bar because browsers silently fix malformed URLs, but the raw HTML itself is technically non-conforming.
Why it matters
- Standards compliance: A valid HTML document requires all attribute values that expect URLs to contain properly encoded URIs. Raw brackets violate this requirement.
- Interoperability: While mainstream browsers handle this gracefully, other HTML consumers — such as screen readers, web scrapers, embedded webview components, or email clients — may not be as forgiving.
- Maintainability: Properly encoded URLs are unambiguous and reduce the risk of subtle parsing bugs, especially when URLs are constructed dynamically or passed through multiple layers of processing.
How to fix it
There are two main approaches:
-
Percent-encode the brackets. Replace every [ with %5B and every ] with %5D in the URL. The server will decode them back to brackets automatically, so functionality is preserved.
-
Use alternative parameter naming. If you control the server, switch to a naming convention that avoids brackets altogether, such as dot notation (filters.category) or underscores (filters_category). This keeps the URL valid without any encoding.
If you’re generating the URL dynamically in JavaScript, you can use encodeURIComponent() on individual parameter keys and values, or use the URL and URLSearchParams APIs, which handle encoding automatically.
Examples
Incorrect — raw brackets in the query string
<iframe src="https://example.com/embed?filters[category]=news&filters[tags]=web"></iframe>
The [ and ] characters in the query string make this an invalid URL value, triggering the validator error.
Fixed — percent-encoded brackets
<iframe src="https://example.com/embed?filters%5Bcategory%5D=news&filters%5Btags%5D=web"></iframe>
Replacing [ with %5B and ] with %5D produces a valid URL. The server receives the same parameter names after decoding.
Fixed — alternative parameter naming
<iframe src="https://example.com/embed?filters.category=news&filters.tags=web"></iframe>
If the server supports it, using dot notation eliminates the need for brackets entirely, keeping the URL clean and valid.
Generating encoded URLs in JavaScript
<iframe id="content-frame"></iframe>
<script>
const url = new URL("https://example.com/embed");
url.searchParams.set("filters[category]", "news");
url.searchParams.set("filters[tags]", "web");
document.getElementById("content-frame").src = url.href;
// Result: https://example.com/embed?filters%5Bcategory%5D=news&filters%5Btags%5D=web
</script>
The URLSearchParams API automatically percent-encodes reserved characters, so you can write the parameter names naturally and let the browser produce a valid URL.
The src attribute on an <iframe> tells the browser which document to load and display within the embedded frame. When you leave this attribute empty (src=""), the W3C HTML Validator reports an error because the HTML specification requires the value to be a valid, non-empty URL. An empty string doesn’t resolve to a meaningful resource.
This is more than a cosmetic validation issue. When a browser encounters src="", it typically interprets the empty string as a relative URL pointing to the current page, which causes the browser to re-fetch and embed the current document inside itself. This leads to unnecessary network requests, potential performance degradation, and unexpected rendering behavior. In some cases it can even cause infinite nesting loops or break page functionality.
From an accessibility standpoint, an empty <iframe> with no valid source provides no meaningful content to assistive technologies. Screen readers may announce the frame without being able to describe its purpose, creating a confusing experience for users.
How to fix it
- Provide a valid URL: Set the src attribute to the actual URL of the content you want to embed.
- Use about:blank for intentionally empty frames: If you need an <iframe> in the DOM but don’t have content to load yet (e.g., you plan to populate it later via JavaScript), use src="about:blank". This is a valid URL that loads a blank page without triggering extra requests.
- Remove the element: If the <iframe> isn’t needed, remove it from the markup entirely. You can dynamically create and insert it with JavaScript when you have content to load.
- Use srcdoc instead: If you want to embed inline HTML rather than loading an external URL, use the srcdoc attribute, which accepts an HTML string directly.
Examples
❌ Empty src attribute
<iframe src=""></iframe>
This triggers the validation error because the src value is an empty string.
❌ src attribute with only whitespace
<iframe src=" "></iframe>
Whitespace-only values are also invalid URLs and will produce the same error.
✅ Valid URL in src
<iframe src="https://example.com/map.html" title="Location map"></iframe>
A fully qualified URL is the most straightforward fix. Note the title attribute, which is recommended for accessibility so that assistive technologies can describe the frame’s purpose.
✅ Using about:blank for a placeholder frame
<iframe src="about:blank" title="Dynamic content area"></iframe>
This is a valid approach when you need the <iframe> in the DOM but plan to set its content later with JavaScript using contentDocument.write() or by updating the src attribute dynamically.
✅ Using srcdoc for inline content
<iframe srcdoc="<p>Hello, embedded world!</p>" title="Inline content"></iframe>
The srcdoc attribute lets you embed HTML directly without needing an external URL. When srcdoc is present, it takes priority over src.
✅ Dynamically creating the iframe with JavaScript
<div id="video-container"></div>
<script>
const iframe = document.createElement("iframe");
iframe.src = "https://example.com/video.html";
iframe.title = "Video player";
document.getElementById("video-container").appendChild(iframe);
</script>
If the source URL isn’t available at page load, creating the <iframe> dynamically avoids having an empty src in your HTML altogether.
A data: URI embeds resource content directly in a URL rather than pointing to an external file. It follows the format data:[<mediatype>][;base64],<data>. RFC 2397, which governs this scheme, explicitly states that fragment identifiers (the # character followed by a fragment name) are not valid in data: URIs.
This issue most commonly arises when developers try to reference a specific element inside an embedded SVG — for example, a particular <symbol> or element with an id — by appending a fragment like #icon-name to a data: URI. While fragments work in standard URLs (e.g., icons.svg#home), the data: URI scheme simply doesn’t support them.
Why It Matters
- Standards compliance: Browsers may handle invalid data: URIs inconsistently. Some may silently ignore the fragment, while others may fail to render the image entirely.
- Portability: Code that relies on non-standard behavior in one browser may break in another or in a future update.
- Accessibility and tooling: Validators, linters, and assistive technologies expect well-formed URIs. An invalid URI can cause unexpected issues down the chain.
How to Fix It
You have several options depending on your use case:
- Remove the fragment from the data: URI. If the embedded content is a complete, self-contained image, it doesn’t need a fragment reference.
- Inline the SVG directly into the HTML document. This lets you reference internal id values with standard fragment syntax using <use> elements.
- Use an external file instead of a data: URI. Standard URLs like icons.svg#home fully support fragment identifiers.
- Encode the full, standalone SVG into the data: URI so that it contains only the content you need, eliminating the need for a fragment reference.
Examples
❌ Incorrect: Fragment in a data: URI
<img
src="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg'%3E%3Csymbol id='icon'%3E%3Ccircle cx='10' cy='10' r='10'/%3E%3C/symbol%3E%3C/svg%3E#icon"
alt="Icon">
The #icon fragment at the end of the data: URI violates RFC 2397 and triggers the validation error.
✅ Correct: Self-contained SVG in a data: URI (no fragment)
Embed only the content you need directly, without wrapping it in a <symbol> or referencing a fragment:
<img
src="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20'%3E%3Ccircle cx='10' cy='10' r='10'/%3E%3C/svg%3E"
alt="Icon">
✅ Correct: External SVG file with a fragment
Move the SVG to a separate file and reference the specific symbol using a standard URL fragment:
<img src="icons.svg#icon" alt="Icon">
✅ Correct: Inline SVG with <use> referencing an internal id
If you need to reference individual symbols from a sprite, inline the SVG and use fragment references within the same document:
<svg xmlns="http://www.w3.org/2000/svg" hidden>
<symbol id="icon-home" viewBox="0 0 20 20">
<path d="M10 2 L2 10 L4 10 L4 18 L16 18 L16 10 L18 10 Z"/>
</symbol>
</svg>
<svg width="20" height="20" aria-hidden="true">
<use href="#icon-home"></use>
</svg>
This approach gives you full control over individual icons without needing data: URIs at all, and it’s the most flexible option for icon systems.
The URL specification (defined by WHATWG and RFC 3986) restricts which characters can appear unencoded in different parts of a URL. Square brackets are reserved characters that have a specific meaning in URLs — they are only permitted in the host portion (to wrap IPv6 addresses like [::1]). In the query string, they must be percent-encoded.
This issue commonly appears when frameworks (especially PHP, Ruby on Rails, or JavaScript libraries) generate URLs with array-style query parameters like ?filter[color]=red. While many browsers and servers are lenient and will interpret these URLs correctly, they are technically invalid according to the URL standard. The W3C validator enforces this rule strictly.
Beyond standards compliance, using unencoded square brackets can cause problems in practice. Some HTTP clients, proxies, or caching layers may reject or mangle URLs containing raw brackets. Percent-encoding these characters ensures your URLs are universally safe and interoperable across all systems.
How to fix it
You have two main approaches:
-
Percent-encode the brackets. Replace every [ with %5B and every ] with %5D in the URL. This preserves the parameter structure that your server or framework expects, while making the URL valid.
-
Restructure the query parameters. If your backend allows it, use flat parameter names with dot notation, underscores, or dashes instead of bracket syntax. For example, change size[width] to size_width or size.width.
If you’re generating URLs in JavaScript, the built-in encodeURI() function does not encode square brackets. Use encodeURIComponent() on individual parameter names or values, or manually replace [ and ] after constructing the URL.
Examples
❌ Invalid: unencoded square brackets in query string
<img src="/images/photo.jpg?size[width]=300&size[height]=200" alt="A sample photo">
The literal [ and ] characters in the query string trigger the validation error.
✅ Fixed: percent-encoded brackets
<img src="/images/photo.jpg?size%5Bwidth%5D=300&size%5Bheight%5D=200" alt="A sample photo">
Replacing [ with %5B and ] with %5D makes the URL valid. Most servers and frameworks will decode these automatically and interpret the parameters the same way.
✅ Fixed: flat parameter names without brackets
<img src="/images/photo.jpg?size_width=300&size_height=200" alt="A sample photo">
If you control the server-side logic, you can avoid brackets altogether by using a flat naming convention for your parameters.
❌ Invalid: brackets in more complex query strings
<img
src="/api/image?filters[type]=jpeg&filters[quality]=80&crop[x]=10&crop[y]=20"
alt="Processed image">
✅ Fixed: all brackets encoded
<img
src="/api/image?filters%5Btype%5D=jpeg&filters%5Bquality%5D=80&crop%5Bx%5D=10&crop%5By%5D=20"
alt="Processed image">
Encoding in JavaScript
If you build image URLs dynamically, handle the encoding in your code:
// Manual replacement approach
const url = "/images/photo.jpg?size[width]=300&size[height]=200";
const safeUrl = url.replace(/\[/g, "%5B").replace(/\]/g, "%5D");
img.src = safeUrl;
// Using URLSearchParams (automatically encodes brackets)
const params = new URLSearchParams();
params.set("size[width]", "300");
params.set("size[height]", "200");
img.src = `/images/photo.jpg?${params.toString()}`;
Both approaches produce a valid, properly encoded URL that will pass W3C validation.
The W3C HTML validator raises this error when the src attribute of an <img> element contains characters that are not permitted in a valid URI. The most common culprit is the < character, but other characters like >, {, }, |, \, ^, and backticks are also illegal in URIs according to RFC 3986.
This issue typically occurs in a few common scenarios:
- Template syntax left unresolved: Server-side or client-side template tags (e.g., <%= imageUrl %>, {{ image.src }}) appear literally in the HTML output instead of being processed into actual URLs.
- Copy-paste errors: HTML markup or angle brackets accidentally end up inside a src value.
- Malformed dynamic URLs: JavaScript or server-side code incorrectly constructs a URL that includes raw HTML or special characters.
This matters because browsers may fail to load the image or interpret the URL unpredictably. Invalid URIs can also cause issues with screen readers and assistive technologies that try to resolve the src to provide context about the image. Keeping your markup standards-compliant ensures consistent behavior across all browsers and environments.
How to fix it
- Check for unprocessed template tags. If you see template syntax like <%, {{, or similar in the rendered HTML, ensure your templating engine is running correctly and outputting the resolved URL.
- Use valid, well-formed URLs. The src value should be a properly formatted absolute or relative URL.
- Percent-encode special characters. If a special character is genuinely part of the URL (which is rare for <), encode it: < becomes %3C, > becomes %3E, and so on.
- Inspect your generated HTML. View the page source in your browser to confirm the actual output, rather than relying on what your code looks like before processing.
Examples
Incorrect — template syntax in src
The template tag was not processed, leaving a < character in the src attribute:
<img src="<%= user.avatar %>" alt="User avatar">
Incorrect — HTML accidentally inside src
Angle brackets from stray markup ended up in the URL:
<img src="images/<thumbnail>/photo.jpg" alt="Photo">
Correct — a valid relative URL
<img src="images/photo.jpg" alt="Photo">
Correct — a valid absolute URL
<img src="https://example.com/images/photo.jpg" alt="Photo">
Correct — special characters percent-encoded
If the URL genuinely requires characters that are not allowed in a URI, percent-encode them:
<img src="https://example.com/search?q=a%3Cb" alt="Search result">
In this case, %3C represents the < character in the query string, making the URI valid.
The HTML specification requires the src attribute of an <img> element to be a valid, non-empty URL. When you set src="", the browser has no resource to fetch, but many browsers will still attempt to make a request — often resolving the empty string relative to the current page URL. This means the browser may re-request the current HTML document as if it were an image, wasting bandwidth and potentially causing unexpected server-side behavior.
Beyond the technical waste, an empty src is problematic for accessibility. Screen readers rely on the <img> element to convey meaningful content. An image with no source provides no value and can confuse assistive technology users. Search engine crawlers may also flag this as a broken resource, negatively affecting SEO.
This issue commonly arises in a few scenarios:
- Placeholder images — developers leave src empty intending to set it later via JavaScript.
- Template rendering — a server-side template or frontend framework outputs an <img> tag before the image URL is available.
- Lazy loading implementations — the real source is stored in a data-src attribute while src is left empty.
How to fix it
The simplest fix is to provide a valid image URL in the src attribute. If the image source isn’t available yet, consider these alternatives:
- Don’t render the <img> element at all until a valid source is available.
- Use a small placeholder image (such as a transparent 1×1 pixel GIF or a lightweight SVG) as a temporary src.
- Use native lazy loading with loading="lazy" and a real src, letting the browser handle deferred loading instead of relying on an empty src.
Examples
❌ Bad: empty src attribute
<img src="" alt="Profile photo">
This triggers the validation error because the src value is an empty string.
❌ Bad: src with only whitespace
<img src=" " alt="Profile photo">
Whitespace-only values are also considered invalid and will produce a similar error.
✅ Good: valid image path
<img src="photo.jpg" alt="Profile photo">
✅ Good: placeholder image for lazy loading
If you’re implementing lazy loading and need a lightweight placeholder, use a small inline data URI or a real placeholder file:
<img
src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7"
data-src="photo.jpg"
alt="Profile photo">
✅ Good: native lazy loading with a real src
Modern browsers support the loading attribute, eliminating the need for an empty src workaround:
<img src="photo.jpg" alt="Profile photo" loading="lazy">
✅ Good: conditionally render the element
If the image URL might not be available, avoid rendering the <img> tag entirely. For example, in a template:
<!-- Only include the img element when a source exists -->
<img src="photo.jpg" alt="Profile photo">
In frameworks like React, Vue, or server-side templating engines, use conditional logic to skip the <img> element when the URL is empty rather than outputting a tag with an empty src.
The src attribute on a <script> element tells the browser where to fetch an external JavaScript file. According to the HTML specification, when the src attribute is present, its value must be a valid non-empty URL. An empty string does not qualify as a valid URL, so the validator flags it as an error.
This issue typically arises in a few common scenarios:
- Templating or CMS placeholders — A template engine or content management system outputs an empty src when no script URL is configured.
- Dynamic JavaScript — Client-side code is intended to set the src later, but the initial HTML ships with an empty value.
- Copy-paste mistakes — The attribute was added in anticipation of a script file that was never specified.
Beyond failing validation, an empty src causes real problems. Most browsers interpret an empty src as a relative URL that resolves to the current page’s URL. This means the browser will make an additional HTTP request to re-fetch the current HTML document and attempt to parse it as JavaScript, wasting bandwidth, slowing down page load, and potentially generating console errors. It can also cause unexpected side effects in server logs and analytics.
How to Fix It
Choose the approach that matches your intent:
- Provide a valid URL — If you need an external script, set src to the correct file path or full URL.
- Use an inline script — If your JavaScript is written directly in the HTML, remove the src attribute entirely. Note that when src is present, browsers ignore any content between the opening and closing <script> tags.
- Remove the element — If the script isn’t needed, remove the <script> element altogether.
Also keep in mind:
- Ensure the file path is correct relative to the HTML file’s location.
- If using an absolute URL, verify it is accessible and returns JavaScript content.
- If a script should only be loaded conditionally, handle the condition in your server-side or build logic rather than outputting an empty src.
Examples
❌ Invalid: Empty src attribute
<script src=""></script>
This triggers the validation error because the src value is an empty string.
❌ Invalid: Whitespace-only src attribute
<script src=" "></script>
A value containing only whitespace is also not a valid URL and will produce the same error.
✅ Fixed: Valid external script
<script src="js/app.js"></script>
The src attribute contains a valid relative URL pointing to an actual JavaScript file.
✅ Fixed: Valid external script with a full URL
<script src="https://example.com/js/library.min.js"></script>
✅ Fixed: Inline script without src
If you want to write JavaScript directly in your HTML, omit the src attribute:
<script>
console.log("This is an inline script.");
</script>
✅ Fixed: Conditionally omitting the element
If the script URL comes from a template variable that might be empty, handle it at the template level so the <script> element is only rendered when a URL is available. For example, in a templating language:
<!-- Pseudocode — only output the tag when the URL exists -->
<!-- {% if script_url %} -->
<script src="analytics.js"></script>
<!-- {% endif %} -->
This prevents the empty src from ever reaching the browser.
The <source> element is used inside <video>, <audio>, and <picture> elements to specify one or more media resources for the browser to choose from. Its src attribute is defined as a valid non-empty URL, meaning it must resolve to an actual file path or web address. An empty string does not satisfy this requirement and violates the HTML specification.
Why this is a problem
Standards compliance: The WHATWG HTML living standard explicitly requires the src attribute on <source> to be a non-empty, valid URL. An empty value makes the document invalid HTML.
Unexpected browser behavior: When a browser encounters an empty src, it may attempt to resolve it relative to the current page URL. This can trigger an unnecessary HTTP request back to the current page, resulting in wasted bandwidth, unexpected server load, or confusing errors in your network logs.
Broken media playback: An empty src means the browser has no media file to load. If the <source> element is the only one provided, the media element will fail to play entirely — often without a clear indication to the user of what went wrong.
Accessibility concerns: Screen readers and assistive technologies rely on well-formed HTML. Invalid markup can lead to unpredictable behavior or missed content announcements for users who depend on these tools.
How to fix it
- Provide a valid URL — Set the src attribute to the correct path or URL of your media file.
- Remove the element — If the media source is not yet available or is being set dynamically via JavaScript, remove the <source> element from the HTML entirely and add it later through script when the URL is known.
- Check for template or CMS issues — This error often appears when a CMS or templating engine outputs a <source> tag with an empty variable. Ensure your template conditionally renders the element only when a valid URL exists.
Examples
Incorrect: empty src on <source> in a video
<video controls>
<source src="" type="video/mp4">
Your browser does not support the video tag.
</video>
Correct: valid URL in src
<video controls>
<source src="movie.mp4" type="video/mp4">
Your browser does not support the video tag.
</video>
Incorrect: empty src on <source> in a picture element
<picture>
<source src="" type="image/webp">
<img src="photo.jpg" alt="A sunset over the ocean">
</picture>
Note that <source> inside <picture> uses srcset, not src. This example is doubly wrong — the attribute is both empty and incorrect. Here is the fix:
Correct: using srcset with a valid URL in a picture element
<picture>
<source srcset="photo.webp" type="image/webp">
<img src="photo.jpg" alt="A sunset over the ocean">
</picture>
Incorrect: multiple sources with one empty src
<audio controls>
<source src="song.ogg" type="audio/ogg">
<source src="" type="audio/mpeg">
</audio>
Correct: remove the source if no file is available
<audio controls>
<source src="song.ogg" type="audio/ogg">
<source src="song.mp3" type="audio/mpeg">
</audio>
Correct: conditionally add sources with JavaScript
If the URL is determined at runtime, avoid placing an empty <source> in your markup. Instead, add it dynamically:
<video id="player" controls>
Your browser does not support the video tag.
</video>
<script>
const videoUrl = getVideoUrl(); // your logic here
if (videoUrl) {
const source = document.createElement("source");
source.src = videoUrl;
source.type = "video/mp4";
document.getElementById("player").appendChild(source);
}
</script>
This approach keeps your HTML valid at all times and only inserts a <source> element when a real URL is available.
The srcset attribute on img and source elements accepts a comma-separated list of image candidate strings. Each candidate consists of a URL optionally followed by a width descriptor (e.g., 300w) or a pixel density descriptor (e.g., 2x). The URL in each candidate must conform to the URL Standard, which does not permit raw square brackets in the query string of an HTTP or HTTPS URL.
This issue commonly arises when a backend framework or CMS generates URLs that use square brackets in query parameters — for example, ?filter[size]=large or ?dimensions[]=300. While many browsers are lenient and will load these URLs anyway, they are technically invalid according to the URL specification. Using invalid URLs can lead to unpredictable behavior across different browsers, HTML parsers, and tools that process your markup. It also means your HTML fails W3C validation, which can mask other, more critical issues in your code.
You have two main approaches to fix this:
-
Percent-encode the brackets. Replace every [ with %5B and every ] with %5D. This preserves the intended query parameter structure while making the URL spec-compliant. Your server should interpret percent-encoded brackets identically to raw brackets.
-
Eliminate brackets from the URL. If you control the server-side code, consider using alternative query parameter conventions that don’t rely on brackets — for instance, using dot notation (filter.size=large), comma-separated values (dimensions=300,400), or repeated parameter names (dimension=300&dimension=400).
When fixing these URLs, also make sure each image candidate follows the correct format: a valid URL, followed by optional whitespace and a descriptor, with candidates separated by commas.
Examples
Incorrect — raw square brackets in query string
This triggers the validation error because [ and ] appear unescaped in the srcset URLs:
<img
src="photo.jpg"
srcset="photo.jpg?crop[width]=400 400w, photo.jpg?crop[width]=800 800w"
sizes="(max-width: 600px) 400px, 800px"
alt="A landscape photo">
Fixed — percent-encoded brackets
Replacing [ with %5B and ] with %5D makes the URLs valid:
<img
src="photo.jpg"
srcset="photo.jpg?crop%5Bwidth%5D=400 400w, photo.jpg?crop%5Bwidth%5D=800 800w"
sizes="(max-width: 600px) 400px, 800px"
alt="A landscape photo">
Fixed — brackets removed from URL design
If you can modify the server-side routing, restructuring the query parameters avoids the issue entirely:
<img
src="photo.jpg"
srcset="photo.jpg?crop_width=400 400w, photo.jpg?crop_width=800 800w"
sizes="(max-width: 600px) 400px, 800px"
alt="A landscape photo">
Incorrect — brackets with pixel density descriptors
The same problem occurs regardless of whether you use width descriptors or density descriptors:
<img
src="avatar.jpg"
srcset="avatar.jpg?size=[sm] 1x, avatar.jpg?size=[lg] 2x"
alt="User avatar">
Fixed — percent-encoded version
<img
src="avatar.jpg"
srcset="avatar.jpg?size=%5Bsm%5D 1x, avatar.jpg?size=%5Blg%5D 2x"
alt="User avatar">
Fixed — simplified query parameters
<img
src="avatar.jpg"
srcset="avatar.jpg?size=sm 1x, avatar.jpg?size=lg 2x"
alt="User avatar">
The srcset attribute allows you to provide multiple image sources so the browser can choose the most appropriate one based on the user’s device and viewport. There are two distinct modes for srcset:
- Width descriptor mode — Each candidate specifies the image’s intrinsic width using a w descriptor (e.g., 640w). This mode requires the sizes attribute so the browser knows how much space the image will occupy in the layout, enabling it to pick the best candidate.
- Density descriptor mode — Each candidate specifies a pixel density using an x descriptor (e.g., 2x). This mode does not use the sizes attribute; the browser simply matches candidates to the device’s pixel density.
These two modes are mutually exclusive. You cannot mix w and x descriptors in the same srcset, and you cannot use x descriptors (or bare URLs with no descriptor) when sizes is present. The HTML specification is explicit about this: if sizes is specified, all image candidate strings must include a width descriptor.
Why this matters
- Standards compliance: The WHATWG HTML Living Standard defines strict parsing rules for srcset. When sizes is present, the browser’s source selection algorithm expects width descriptors. Providing density descriptors or bare candidates in this context violates the spec and produces undefined behavior.
- Broken image selection: Browsers rely on the sizes attribute to calculate which w-described image best fits the current layout width. If you provide x descriptors alongside sizes, the browser may ignore the srcset entirely or fall back to the src attribute, defeating the purpose of responsive images.
- Accessibility and performance: Responsive images exist to serve appropriately sized files to different devices. An invalid srcset/sizes combination can result in oversized images being downloaded on small screens (wasting bandwidth) or undersized images on large screens (reducing visual quality).
How to fix it
You have two options:
- Keep sizes and switch to width descriptors — Replace every x descriptor (or missing descriptor) in srcset with the actual intrinsic pixel width of each image file, expressed with a w suffix.
- Remove sizes and keep density descriptors — If you only need to serve different resolutions for high-DPI screens at a fixed layout size, drop the sizes attribute and use x descriptors.
When using width descriptors, the value must match the image file’s actual intrinsic width in pixels. For example, if photo-640.jpg is 640 pixels wide, its descriptor should be 640w.
Examples
Invalid: sizes present with density descriptors
This triggers the error because 1x and 2x are density descriptors, but sizes requires width descriptors.
<img
src="photo-640.jpg"
srcset="photo-640.jpg 1x, photo-1280.jpg 2x"
sizes="(max-width: 600px) 100vw, 600px"
alt="A mountain landscape">
Invalid: sizes present with a bare candidate (no descriptor)
A candidate with no descriptor defaults to 1x, which is a density descriptor — still invalid when sizes is present.
<img
src="photo-640.jpg"
srcset="photo-640.jpg, photo-1280.jpg 2x"
sizes="(max-width: 600px) 100vw, 600px"
alt="A mountain landscape">
Fix: use width descriptors with sizes
Each candidate now specifies the intrinsic width of the image file. The browser uses the sizes value to determine which image to download.
<img
src="photo-640.jpg"
srcset="photo-320.jpg 320w, photo-640.jpg 640w, photo-1280.jpg 1280w"
sizes="(max-width: 600px) 100vw, 600px"
alt="A mountain landscape">
Alternative fix: remove sizes and use density descriptors
If you don’t need the browser to choose images based on layout width — for example, the image always renders at a fixed CSS size — drop sizes and use x descriptors.
<img
src="photo-640.jpg"
srcset="photo-640.jpg 1x, photo-1280.jpg 2x"
alt="A mountain landscape">
Using width descriptors with source inside picture
The same rule applies to source elements inside a picture. If sizes is present, every candidate must use a w descriptor.
<picture>
<source
srcset="hero-480.webp 480w, hero-960.webp 960w, hero-1920.webp 1920w"
sizes="(max-width: 768px) 100vw, 50vw"
type="image/webp">
<img
src="hero-960.jpg"
srcset="hero-480.jpg 480w, hero-960.jpg 960w, hero-1920.jpg 1920w"
sizes="(max-width: 768px) 100vw, 50vw"
alt="A hero banner image">
</picture>
The srcset attribute supports two types of descriptors: width descriptors (like 480w) and pixel density descriptors (like 2x). However, these two types cannot be mixed, and the sizes attribute is only compatible with width descriptors. The sizes attribute tells the browser how wide the image will be displayed at various viewport sizes, and the browser uses this information along with the width descriptors in srcset to choose the most appropriate image file. If sizes is present but an image candidate lacks a width descriptor, the browser cannot perform this calculation correctly.
This matters for several reasons. First, it violates the WHATWG HTML specification, which explicitly requires that when sizes is present, all image candidates must use width descriptors. Second, browsers may ignore malformed srcset values or fall back to unexpected behavior, resulting in the wrong image being loaded — potentially hurting performance by downloading unnecessarily large files or degrading visual quality by selecting a too-small image. Third, standards-compliant markup ensures consistent, predictable behavior across all browsers and devices.
A common mistake is specifying a plain URL without any descriptor, or mixing density descriptors (1x, 2x) with the sizes attribute. An image candidate string without any descriptor defaults to 1x, which is a density descriptor — and that conflicts with the presence of sizes.
Examples
❌ Incorrect: Missing width descriptor with sizes present
<picture>
<source
srcset="image-small.jpg, image-large.jpg 1024w"
sizes="(max-width: 600px) 480px, 800px">
<img src="image-fallback.jpg" alt="A scenic landscape">
</picture>
Here, image-small.jpg has no width descriptor. Since sizes is present, this triggers the validation error.
❌ Incorrect: Using density descriptors with sizes
<img
srcset="image-1x.jpg 1x, image-2x.jpg 2x"
sizes="(max-width: 600px) 480px, 800px"
src="image-fallback.jpg"
alt="A scenic landscape">
Density descriptors (1x, 2x) are incompatible with the sizes attribute.
✅ Correct: All candidates have width descriptors
<picture>
<source
srcset="image-small.jpg 480w, image-large.jpg 1024w"
sizes="(max-width: 600px) 480px, 800px">
<img src="image-fallback.jpg" alt="A scenic landscape">
</picture>
Every image candidate now includes a width descriptor, which pairs correctly with the sizes attribute.
✅ Correct: Using density descriptors without sizes
If you want to use density descriptors instead of width descriptors, simply remove the sizes attribute:
<img
srcset="image-1x.jpg 1x, image-2x.jpg 2x"
src="image-fallback.jpg"
alt="A scenic landscape">
This is valid because density descriptors don’t require (and shouldn’t be used with) the sizes attribute.
✅ Correct: Width descriptors on <img> with sizes
<img
srcset="photo-320.jpg 320w, photo-640.jpg 640w, photo-1280.jpg 1280w"
sizes="(max-width: 400px) 320px, (max-width: 800px) 640px, 1280px"
src="photo-640.jpg"
alt="A close-up of a flower">
Each entry in srcset specifies its intrinsic width, and sizes tells the browser which display width to expect at each breakpoint. The browser then selects the best-fitting image automatically.
The tabindex attribute controls whether and in what order an element can receive keyboard focus. The W3C validator reports this error when it encounters tabindex="" because the HTML specification requires the attribute’s value to be a valid integer — and an empty string cannot be parsed as one. This commonly happens when a value is accidentally omitted, when a template engine outputs a blank value, or when a CMS generates the attribute without a proper default.
Why this matters
Keyboard navigation is fundamental to web accessibility. Screen readers and assistive technologies rely on tabindex values to determine focus behavior. An empty tabindex attribute creates ambiguity: browsers may ignore it or handle it inconsistently, leading to unpredictable focus behavior for keyboard and assistive technology users. Beyond accessibility, it also means your HTML is invalid according to the WHATWG HTML living standard, which strictly defines tabindex as accepting only a valid integer.
How tabindex works
The attribute accepts an integer value with three meaningful ranges:
- Negative value (e.g., tabindex="-1"): The element can be focused programmatically (via JavaScript’s .focus()), but it is excluded from the sequential keyboard tab order.
- tabindex="0": The element is added to the natural tab order based on its position in the document source. This is the most common way to make non-interactive elements (like a <div> or <span>) keyboard-focusable.
- Positive value (e.g., tabindex="1", tabindex="5"): The element is placed in the tab order ahead of elements with tabindex="0" or no tabindex, with lower numbers receiving focus first. Using positive values is generally discouraged because it overrides the natural document order and makes focus management harder to maintain.
How to fix it
- If the element should be focusable in tab order, set tabindex="0".
- If the element should only be focusable programmatically, set tabindex="-1".
- If you don’t need custom focus behavior, remove the tabindex attribute entirely. Interactive elements like <a>, <button>, and <input> are already focusable by default.
- If a template or CMS generates the attribute, ensure the logic provides a valid integer or omits the attribute when no value is available.
Examples
❌ Empty tabindex value (triggers the error)
<div tabindex="">Click me</div>
<button tabindex="">Submit</button>
✅ Fixed: valid integer provided
<div tabindex="0">Click me</div>
<button tabindex="-1">Submit</button>
✅ Fixed: attribute removed when not needed
Interactive elements like <button> are focusable by default, so tabindex can simply be removed:
<button>Submit</button>
✅ Fixed: conditional rendering in a template
If you’re using a templating system, ensure the attribute is only rendered when a valid value exists:
<!-- Instead of always outputting tabindex -->
<!-- Bad: <div tabindex="{{ tabindexValue }}"> -->
<!-- Only output the attribute when a value is set -->
<div tabindex="0">Interactive content</div>
A note on positive tabindex values
While positive integers like tabindex="1" or tabindex="3" are technically valid, they force a custom tab order that diverges from the visual and DOM order of the page. This creates confusion for keyboard users and is difficult to maintain as pages evolve. The WAI-ARIA Authoring Practices recommend avoiding positive tabindex values. Stick with 0 and -1 in nearly all cases.
The target attribute specifies where a linked document should be opened. When the validator encounters target="", it reports an error because the HTML specification requires browsing context names to be at least one character long. An empty string is not a valid browsing context name and has no defined behavior, which means browsers may handle it inconsistently.
This issue commonly arises when a target value is dynamically generated by a CMS, template engine, or JavaScript and the value ends up being empty. It can also happen when a developer adds the attribute with the intent to fill it in later but forgets to do so.
Setting target to an empty string is problematic for several reasons:
- Standards compliance: The WHATWG HTML specification explicitly requires valid browsing context names to be non-empty strings. An empty value violates this rule.
- Unpredictable behavior: Browsers may interpret an empty target differently — some may treat it like _self, others may behave unexpectedly. This makes your site harder to test and maintain.
- Accessibility concerns: Screen readers and assistive technologies may announce the target attribute or use it to inform users about link behavior. An empty value provides no meaningful information.
To fix this, choose one of the following approaches:
- Remove the attribute if you want the default behavior (opening in the same browsing context, equivalent to _self).
- Set a valid keyword like _blank, _self, _parent, or _top.
- Set a custom name if you want multiple links to share the same browsing context (e.g., target="externalWindow").
Examples
❌ Invalid: empty target attribute
<a href="https://example.com" target="">Visit Example</a>
This triggers the validation error because the target value is an empty string.
✅ Fixed: remove target entirely
If you want the link to open in the current browsing context (the default), simply remove the attribute:
<a href="https://example.com">Visit Example</a>
✅ Fixed: use _blank to open in a new tab
<a href="https://example.com" target="_blank" rel="noopener">Visit Example</a>
Note the addition of rel="noopener" — this is a security best practice when using target="_blank", as it prevents the opened page from accessing the window.opener property.
✅ Fixed: use _self explicitly
If you want to be explicit about opening in the same context:
<a href="https://example.com" target="_self">Visit Example</a>
✅ Fixed: use a custom browsing context name
You can use a custom name so that multiple links reuse the same tab or window:
<a href="https://example.com" target="externalWindow">Example</a>
<a href="https://example.org" target="externalWindow">Example Org</a>
Both links will open in the same browsing context named externalWindow. If it doesn’t exist yet, the browser creates it; subsequent clicks reuse it.
Dynamic templates
If your target value comes from a template or CMS, make sure the attribute is conditionally rendered rather than output with an empty value. For example, in a templating language:
<!-- Instead of always outputting target -->
<a href="https://example.com" target="">Visit</a>
<!-- Only include target when a value exists -->
<a href="https://example.com" target="_blank" rel="noopener">Visit</a>
In your template logic, conditionally omit the target attribute entirely when no value is provided, rather than rendering it as an empty string.
The target attribute on the <area> element tells the browser where to display the linked resource — in the current tab, a new tab, a parent frame, or a named <iframe>. According to the WHATWG HTML living standard, a valid navigable target must be either a keyword beginning with an underscore (_self, _blank, _parent, _top) or a name that is at least one character long. An empty string ("") satisfies neither condition, so the W3C validator reports:
Bad value “” for attribute “target” on element “area”: Browsing context name must be at least one character long.
This commonly happens when templating engines or CMS platforms output target="" as a default, or when a value is conditionally set but the logic fails to produce a result.
Why this matters
- Standards compliance. An empty target violates the HTML specification and produces a validation error.
- Unpredictable browser behavior. While most browsers treat an empty target the same as _self, this is not guaranteed by the spec. Relying on undefined behavior can lead to inconsistencies across browsers or future versions.
- Code clarity. An empty target signals unclear intent. Removing it or using an explicit keyword makes the code easier to understand and maintain.
How to fix it
- Remove the target attribute if you want the link to open in the same browsing context. This is the default behavior, equivalent to target="_self".
- Use a valid keyword like _self, _blank, _parent, or _top if you need specific navigation behavior.
- Use a named browsing context (e.g., target="contentFrame") if you want to direct the link to a specific <iframe> or window. The name must be at least one character long.
- Fix your template logic if the empty value is being generated dynamically. Ensure the target attribute is only rendered when a non-empty value is available.
Examples
Invalid: empty target attribute
This triggers the validation error because target is set to an empty string:
<img src="floor-plan.png" usemap="#rooms" alt="Floor plan">
<map name="rooms">
<area shape="rect" coords="10,10,100,60" href="/kitchen" alt="Kitchen" target="">
</map>
Fixed: remove target for default behavior
If you want the link to open in the same tab (the default), simply remove the target attribute:
<img src="floor-plan.png" usemap="#rooms" alt="Floor plan">
<map name="rooms">
<area shape="rect" coords="10,10,100,60" href="/kitchen" alt="Kitchen">
</map>
Fixed: use a valid keyword
Use _self to be explicit about same-tab navigation, or _blank to open in a new tab:
<img src="floor-plan.png" usemap="#rooms" alt="Floor plan">
<map name="rooms">
<area shape="rect" coords="10,10,100,60" href="/kitchen" alt="Kitchen" target="_self">
<area shape="rect" coords="110,10,200,60" href="/bedroom" alt="Bedroom" target="_blank">
</map>
Fixed: target a named <iframe>
If you want to load the linked resource into a specific <iframe>, give the <iframe> a name attribute and reference it in target:
<iframe name="detailView" src="about:blank" title="Room details"></iframe>
<img src="floor-plan.png" usemap="#rooms" alt="Floor plan">
<map name="rooms">
<area shape="rect" coords="10,10,100,60" href="/kitchen" alt="Kitchen" target="detailView">
</map>
Fixed: conditionally render target in templates
If your target value comes from a variable, make sure the attribute is only output when the value is non-empty. For example, in a Jinja-style template:
<area shape="rect" coords="10,10,100,60" href="/kitchen" alt="Kitchen"
{% if target_value %} target="{{ target_value }}"{% endif %}>
This prevents target="" from appearing in your HTML when no value is set.
The type attribute on an anchor (<a>) element is an advisory hint that tells the browser what kind of content to expect at the link destination. According to the WHATWG HTML specification, when present, this attribute must contain a valid MIME type string. An empty string ("") is not a valid MIME type, so the W3C validator flags it as an error.
This issue commonly appears when a CMS, templating engine, or JavaScript framework generates the type attribute dynamically but produces an empty value when no MIME type is available. It can also happen when developers add the attribute as a placeholder intending to fill it in later.
Why this matters
While browsers generally handle an empty type attribute gracefully by ignoring it, there are good reasons to fix this:
- Standards compliance — An empty string violates the HTML specification’s requirement for a valid MIME type, making your document invalid.
- Accessibility — Assistive technologies may use the type attribute to communicate the linked resource’s format to users. An empty value provides no useful information and could lead to unexpected behavior in some screen readers.
- Predictable behavior — Browsers are allowed to use the type hint to influence how they handle a link (e.g., suggesting a download or choosing a handler). An empty value makes the intent ambiguous.
How to fix it
You have two straightforward options:
- Remove the type attribute — If you don’t need to specify the MIME type, simply omit the attribute. This is the preferred approach when the type is unknown or unnecessary.
- Provide a valid MIME type — If you want to hint at the linked resource’s format, supply a proper MIME type string like application/pdf, text/html, image/png, application/zip, etc.
The type attribute is purely advisory — the browser does not enforce it or refuse to follow the link if the actual content type differs. So omitting it is always safe.
Examples
Incorrect — empty type attribute
<a href="report.pdf" type="">Download Report</a>
This triggers the validation error because "" is not a valid MIME type.
Correct — attribute removed
<a href="report.pdf">Download Report</a>
Removing the type attribute entirely is the simplest fix and works perfectly when the MIME type hint isn’t needed.
Correct — valid MIME type provided
<a href="report.pdf" type="application/pdf">Download Report</a>
If you want to explicitly indicate the format of the linked resource, use a proper MIME type value.
Multiple links with correct type usage
<ul>
<li><a href="slides.pptx" type="application/vnd.openxmlformats-officedocument.presentationml.presentation">Slides (PPTX)</a></li>
<li><a href="data.csv" type="text/csv">Data (CSV)</a></li>
<li><a href="https://example.com/about">About Us</a></li>
</ul>
In this example, the first two links include type attributes with valid MIME types to hint at the file format. The third link, pointing to a regular webpage, omits type entirely since it’s not needed.
Fixing dynamically generated attributes
If your template engine produces empty type attributes, add a conditional check:
<!-- Instead of always outputting type="" -->
<!-- Only include the attribute when a value exists -->
<a href="report.pdf" type="application/pdf">Download Report</a>
In template logic, ensure you only render the type attribute when a non-empty MIME type value is available. Otherwise, omit the attribute from the output altogether.
The HTML specification defines that the width and height attributes on <iframe> elements must contain a valid non-negative integer — that is, a string of one or more digits representing a number zero or greater (e.g., "0", "300", "600"). When one of these attributes is set to an empty string (width="" or height=""), the validator raises this error because an empty string cannot be parsed as a valid integer.
This commonly happens when a CMS, template engine, or JavaScript framework outputs an <iframe> with a dynamic dimension value that ends up being blank. It can also occur when developers remove the value but leave the attribute in place, or when copy-pasting embed code and accidentally clearing the value.
While most browsers will fall back to their default iframe dimensions (typically 300×150 pixels) when they encounter an empty value, relying on this behavior is not standards-compliant. Invalid attribute values can cause unpredictable rendering across different browsers, interfere with layout calculations, and make your markup harder to maintain. Assistive technologies may also have trouble determining the intended dimensions of the iframe.
How to fix it
You have a few options:
- Set a valid integer value. If you know the desired dimensions, specify them directly as non-negative integers. The values represent pixels.
- Remove the attribute entirely. If you don’t need to set dimensions via HTML attributes, remove the empty width or height attribute. The browser will apply its default size, or you can control sizing with CSS.
- Use CSS instead. For responsive designs or more flexible sizing, remove the HTML attributes and use CSS properties like width, height, max-width, or aspect-ratio.
Note that these attributes accept only plain integers — no units, no percentages, and no decimal points. For example, width="600" is valid, but width="600px" or width="100%" is not.
Examples
❌ Invalid: empty string values
<iframe src="https://example.com" width="" height=""></iframe>
Both width and height are set to empty strings, which are not valid non-negative integers.
✅ Fixed: specify valid integer values
<iframe src="https://example.com" width="600" height="400"></iframe>
✅ Fixed: remove the empty attributes
<iframe src="https://example.com"></iframe>
The browser will use its default dimensions (typically 300×150 pixels).
✅ Fixed: remove attributes and use CSS for sizing
<iframe src="https://example.com" style="width: 100%; height: 400px;"></iframe>
This approach is especially useful for responsive layouts where a fixed pixel width in HTML doesn’t make sense.
✅ Fixed: responsive iframe with CSS aspect ratio
<iframe
src="https://example.com/video"
style="width: 100%; aspect-ratio: 16 / 9; border: none;">
</iframe>
Using aspect-ratio in CSS lets the iframe scale responsively while maintaining its proportions, without needing width or height attributes at all.
The HTML specification requires that the width and height attributes on <img> elements, when present, contain a string representing a non-negative integer — that is, a sequence of one or more ASCII digits like "0", "150", or "1920". An empty string ("") does not satisfy this requirement, so the W3C validator flags it as an error.
This issue commonly arises when:
- A CMS or templating engine outputs width="" or height="" because no dimension value was configured.
- JavaScript dynamically sets img.setAttribute("width", "") instead of removing the attribute.
- A developer adds the attributes as placeholders intending to fill them in later but forgets to do so.
Why it matters
Providing valid width and height attributes is one of the most effective ways to prevent Cumulative Layout Shift (CLS). Browsers use these values to calculate the image’s aspect ratio and reserve the correct amount of space before the image loads. When the values are empty strings, the browser cannot determine the aspect ratio, so no space is reserved — leading to layout shifts as images load in, which hurts both user experience and Core Web Vitals scores.
Beyond performance, invalid attribute values can cause unpredictable rendering behavior across browsers. Some browsers may ignore the attribute, others may interpret the empty string as 0, collapsing the image to zero pixels in that dimension. Standards-compliant HTML also improves accessibility by ensuring assistive technologies can parse the document reliably.
Examples
❌ Invalid: empty string values
<img src="photo.jpg" alt="A sunset over the ocean" width="" height="">
Both width and height are set to empty strings, which is not valid.
✅ Fixed: provide actual dimensions
<img src="photo.jpg" alt="A sunset over the ocean" width="800" height="600">
Replace the empty strings with the image’s actual pixel dimensions. These values should reflect the image’s intrinsic (natural) size. CSS can still be used to scale the image visually — the browser will use the width and height ratio to reserve the correct space.
✅ Fixed: remove the attributes entirely
<img src="photo.jpg" alt="A sunset over the ocean">
If you don’t know the dimensions or prefer to handle sizing purely through CSS, remove the attributes altogether. An absent attribute is valid; an empty one is not.
❌ Invalid: only one attribute is empty
<img src="banner.jpg" alt="Promotional banner" width="1200" height="">
Even if only one attribute has an empty value, the validation error will be triggered for that attribute.
✅ Fixed: both attributes with valid values
<img src="banner.jpg" alt="Promotional banner" width="1200" height="400">
Fixing dynamic/template-generated markup
If a template language is outputting empty attributes, use a conditional to omit them when no value is available. For example, in a template:
<!-- Instead of always outputting the attributes: -->
<img src="photo.jpg" alt="Description" width="" height="">
<!-- Conditionally include them only when values exist: -->
<img src="photo.jpg" alt="Description" width="800" height="600">
If you’re setting dimensions via JavaScript, remove the attribute rather than setting it to an empty string:
// ❌ Don't do this
img.setAttribute("width", "");
// ✅ Do this instead
img.removeAttribute("width");
// ✅ Or set a valid value
img.setAttribute("width", "800");
A note on values
The width and height attributes only accept non-negative integers — whole numbers without units, decimals, or percentage signs. Values like "100px", "50%", or "3.5" are also invalid. Use plain integers like "100" or "600". If you need responsive sizing with percentages or other CSS units, apply those through CSS styles instead.
The xmlns attribute declares the XML namespace for an element. For SVG elements, the only permitted namespace is "http://www.w3.org/2000/svg". When this attribute is present but set to an empty string ("") or any value other than the correct namespace, the W3C validator reports an error because the browser cannot properly associate the element with the SVG specification.
In HTML5 documents (served as text/html), the xmlns attribute on <svg> is actually optional. The HTML parser automatically associates <svg> elements with the correct SVG namespace without needing an explicit declaration. However, if you do include the xmlns attribute — for example, because your SVG was exported from a design tool or copied from an XML-based source — it must contain the exact value "http://www.w3.org/2000/svg". An empty or incorrect value will cause a validation error and could lead to rendering issues in certain contexts.
This matters for several reasons:
- Standards compliance: The HTML specification explicitly restricts the allowed value for xmlns on SVG elements.
- Browser compatibility: While most modern browsers are forgiving in HTML mode, an incorrect namespace can cause problems when SVG content is used in XML contexts (such as XHTML or standalone .svg files).
- Interoperability: Tools and libraries that process your HTML may rely on the correct namespace to identify and manipulate SVG elements.
To fix this issue, you have two options:
- Set the correct value: Replace the empty or incorrect xmlns value with "http://www.w3.org/2000/svg".
- Remove the attribute entirely: Since xmlns is optional in HTML5, simply removing it is often the cleanest solution.
Examples
Incorrect: empty xmlns attribute
This triggers the validation error because the namespace value is an empty string:
<svg xmlns="" width="100" height="100">
<circle cx="50" cy="50" r="40" fill="red" />
</svg>
Incorrect: wrong namespace value
Any value other than the correct SVG namespace will also trigger this error:
<svg xmlns="http://www.w3.org/2000/html" width="100" height="100">
<circle cx="50" cy="50" r="40" fill="red" />
</svg>
Fix: use the correct namespace
Set xmlns to the only permitted value:
<svg xmlns="http://www.w3.org/2000/svg" width="100" height="100">
<circle cx="50" cy="50" r="40" fill="red" />
</svg>
Fix: remove the xmlns attribute
In an HTML5 document, you can omit xmlns altogether since the parser handles the namespace automatically:
<svg width="100" height="100">
<circle cx="50" cy="50" r="40" fill="red" />
</svg>
Note on inline SVG from external sources
Design tools like Figma, Illustrator, or Inkscape often export SVG files with the xmlns attribute already set correctly. If you’re copying SVG markup and the xmlns value gets accidentally cleared or corrupted during the process, either restore it to "http://www.w3.org/2000/svg" or remove it before embedding the SVG in your HTML. Both approaches will produce valid, working markup.
The W3C HTML specification restricts which ARIA roles can be used on specific elements. An li element already carries the implicit role of listitem when it’s a child of a ul or ol, so adding an explicit role is often unnecessary. The group role, as defined in the WAI-ARIA specification, is meant for container elements that form a logical collection of related items — similar to how a fieldset groups form controls. Applying group to an li element contradicts the element’s purpose as an individual item within a list.
This matters for several reasons. Assistive technologies like screen readers rely on ARIA roles to communicate the structure and purpose of content to users. When an li element is marked with role="group", it overrides the native listitem semantics, potentially confusing users who depend on these tools. A screen reader might announce the element as a group container rather than a list item, breaking the expected navigation pattern of the list. Browsers and assistive technologies are optimized around correct role usage, so invalid combinations can lead to unpredictable behavior.
When you might actually need group
If your intent is to group a set of related elements together, the group role belongs on a wrapping container — not on the individual items inside it. For example, you might use role="group" on a div or a ul that contains a subset of related controls within a larger interface. If an li contains nested interactive content that needs to be grouped, wrap that content in an inner container with the group role instead.
How to fix it
- Remove the role entirely if the li is a standard list item inside a ul or ol. The browser already provides the correct listitem semantics.
- Move the group role to a container element if you genuinely need to create a labeled group of related items.
- Use a different valid role if the li serves a non-standard purpose, such as menuitem, option, tab, treeitem, or presentation, depending on the widget pattern you’re building.
Examples
Incorrect: group role on li elements
<ul>
<li role="group">Fruits</li>
<li role="group">Vegetables</li>
<li role="group">Grains</li>
</ul>
This triggers the validation error because group is not a permitted role for li.
Correct: no explicit role needed
<ul>
<li>Fruits</li>
<li>Vegetables</li>
<li>Grains</li>
</ul>
Each li inside a ul automatically has the listitem role. No additional markup is needed.
Correct: using group on a container element
If you need to group related items with a label, apply the group role to the container:
<div role="group" aria-labelledby="food-heading">
<h2 id="food-heading">Food Categories</h2>
<ul>
<li>Fruits</li>
<li>Vegetables</li>
<li>Grains</li>
</ul>
</div>
Correct: nested groups within list items
If each list item contains a group of related controls, place the group role on an inner wrapper:
<ul>
<li>
<div role="group" aria-label="Text formatting">
<button>Bold</button>
<button>Italic</button>
<button>Underline</button>
</div>
</li>
<li>
<div role="group" aria-label="Text alignment">
<button>Left</button>
<button>Center</button>
<button>Right</button>
</div>
</li>
</ul>
This preserves the list structure while correctly applying the group role to containers of related widgets. The aria-label attribute gives each group an accessible name, which is recommended when using role="group".
A space before the width attribute value is causing the validator to misparse the iframe attributes, likely due to a missing closing quote on the width attribute.
This error typically occurs when there’s a typo in the attribute syntax, such as a missing closing quote or extra spaces inside the attribute value. The validator reads the raw HTML and interprets " height= as part of the width value because the width attribute’s opening quote was never properly closed.
The width and height attributes on an iframe element accept non-negative integer values representing pixels. Each attribute must have its value properly quoted and contain only digits.
HTML Examples
❌ Incorrect
<iframe src="page.html" width="600 height="400"></iframe>
In this example, the closing quote after 600 is missing. The validator sees the width value as 600 height=, which is not a valid number.
✅ Correct
<iframe src="page.html" width="600" height="400"></iframe>
Each attribute has properly matched opening and closing quotes, and the values contain only digits.
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