Validation errors
Catalogue of validation errors and warnings raised during theme upload
This reference documents every validation error and warning that the theme validator can produce. Entries are organised by severity: errors first (must be fixed before a theme can be published), then warnings (advisory, non-blocking). Each entry includes the error code, the message shown, the cause, and the fix.
The validator runs in two contexts:
- Upload validation -- runs server-side when a theme ZIP is uploaded via the CMS.
- Local validation -- runs via
localstage theme checkduring development.
Both validators share the same rule set. The error codes listed below apply to both contexts.
Overview
| Code | Severity | Category |
|---|---|---|
| missing-layouts | Error | Manifest |
| missing-default-layout | Error | Manifest |
| block-missing-schema | Error | Schema |
| block-duplicate-name | Warning | Schema |
| invalid-schema-json | Error | Schema |
| unsupported-field-type | Warning | Schema |
| unsupported-asset-type | Error | Asset Integrity |
| missing-asset-reference | Error | Asset Integrity |
| invalid-layout-reference | Error | Manifest |
| missing-schema-tag | Warning | Schema |
| multiple-schema-tags | Warning | Schema |
| orphaned-assets | Warning | Asset Integrity |
| large-asset | Warning | Best Practice |
| deprecated-include | Warning | Compatibility |
| missing-meta-description | Warning | Best Practice |
| deep-nesting | Warning | Best Practice |
Errors
missing-layouts
Severity: Error
Category: Manifest Completeness
Message: Missing required directory: layouts.
Cause: The theme ZIP does not contain a layouts/ directory. Every theme must include at least one layout file.
Fix: Create a layouts/ directory in the root of the theme and add at least default.liquid.
Example:
Before (incorrect directory structure):
my-theme/
templates/
page.liquid
blocks/
hero.liquidAfter (correct directory structure):
my-theme/
layouts/
default.liquid
templates/
page.liquid
blocks/
hero.liquidSee also:
missing-default-layout
Severity: Error
Category: Manifest Completeness
Message: Missing layouts/default.liquid.
Cause: The layouts/ directory exists but does not contain default.liquid. This file is the fallback layout used when a template does not specify a layout via {% layout %}.
Fix: Create layouts/default.liquid with a {{ content_for_layout }} output tag.
Example:
<!DOCTYPE html>
<html>
<head>
<title>{{ page.title }}</title>
{{ "styles.css" | asset_url | stylesheet_tag }}
</head>
<body>
{{ content_for_layout }}
</body>
</html>See also:
block-missing-schema
Severity: Error
Category: Schema Validation
Message: Block is missing a schema tag. / Block schema is missing.
Cause: A .liquid file in blocks/ does not contain a {% schema %}...{% endschema %} tag. Every block file must declare its schema so the CMS can generate the corresponding editor fields.
Fix: Add a {% schema %} tag containing valid JSON with at minimum name and settings properties.
Example:
Before:
<section class="hero">
<h1>{{ block.heading }}</h1>
</section>After:
<section class="hero">
<h1>{{ block.heading }}</h1>
</section>
{% schema %}
{
"name": "hero",
"label": "Hero",
"singular": "Hero",
"plural": "Heroes",
"settings": [
{
"name": "heading",
"type": "text",
"label": "Heading"
}
]
}
{% endschema %}See also:
block-duplicate-name
Severity: Warning
Category: Schema Validation
Message: Duplicate block name '<name>' (also in <file>).
Cause: Two or more block files declare the same name value in their {% schema %} tag. Block names are used as unique identifiers when templates reference blocks via allowed_blocks.
Fix: Ensure every block has a unique name value in its schema. Rename one of the conflicting blocks.
Example:
Before (blocks/hero.liquid and blocks/hero-alt.liquid both use "name": "hero"):
{
"name": "hero",
"settings": []
}After (rename the second block):
{
"name": "hero-alternate",
"settings": []
}invalid-schema-json
Severity: Error
Category: Schema Validation
Message: Invalid block schema JSON: <parse error> / Invalid template schema JSON: <parse error> / Schema tag is empty.
Cause: The content between {% schema %} and {% endschema %} is not valid JSON. Common causes include trailing commas, unquoted keys, missing closing brackets, or an entirely empty schema tag.
Fix: Validate the JSON syntax. Use a JSON linter or paste the schema content into a validator. Ensure all keys are double-quoted, brackets are balanced, and there are no trailing commas.
Example:
Before (trailing comma):
{% schema %}
{
"name": "hero",
"settings": [
{ "name": "heading", "type": "text" },
]
}
{% endschema %}After (trailing comma removed):
{% schema %}
{
"name": "hero",
"settings": [
{ "name": "heading", "type": "text" }
]
}
{% endschema %}See also:
unsupported-field-type
Severity: Warning
Category: Schema Validation
Message: Unsupported field type "<type>".
Cause: A field in the schema settings array uses a type value that is not in the allowed list. The validator will report this but will not block the upload.
Fix: Use one of the 20 allowed field types:
| Type | Description |
|---|---|
text | Single-line text input |
textArea | Multi-line text input |
email | Email address input |
url | URL input |
number | Numeric input |
checkbox | Boolean checkbox |
switch | Boolean toggle switch |
color | Colour picker |
date | Date picker |
dateTime | Date and time picker |
radio | Radio button group |
select | Dropdown select |
code | Code editor |
json | JSON editor |
richText | Rich text editor (the rich text editor) |
hidden | Hidden field |
relationship | Relationship to another document |
upload | File upload |
array | Repeatable group of fields |
group | Static group of nested fields |
The validator also accepts common case variations and normalises them automatically: richtext becomes richText, textarea becomes textArea, datetime becomes dateTime.
Example:
Before:
{ "name": "colour", "type": "colorPicker" }After:
{ "name": "colour", "type": "color" }unsupported-asset-type
Severity: Error
Category: Asset Integrity
Message: Asset type .<extension> is not allowed.
Cause: A file in the assets/ directory uses a file extension that is not on the allowed list.
Fix: Only use files with the following extensions in assets/:
css, eot, gif, ico, jpg, jpeg, js, json, png, svg, ttf, webp, woff, woff2
Convert or remove files with disallowed extensions. For example, convert .bmp images to .png or .webp.
Example:
Before:
assets/
background.bmp
logo.tiffAfter:
assets/
background.webp
logo.pngmissing-asset-reference
Severity: Error
Category: Asset Integrity
Message: Missing asset assets/<path>.
Cause: A template references an asset file via the asset_url filter or a direct path, but the referenced file does not exist in the assets/ directory.
Fix: Either add the missing file to assets/ or correct the path in the template.
Example:
Before (template references a file that does not exist):
{{ "styles/main.css" | asset_url | stylesheet_tag }}assets/
style.css <-- filename does not matchAfter (correct the filename or the reference):
{{ "style.css" | asset_url | stylesheet_tag }}invalid-layout-reference
Severity: Error
Category: Manifest Completeness
Message: Layout reference "<name>" does not exist.
Cause: A template uses {% layout '<name>' %} but the referenced layout file does not exist in the layouts/ directory.
Fix: Either create the missing layout file or update the {% layout %} tag to reference an existing layout. If the template should use the default layout, remove the {% layout %} tag entirely or use {% layout 'default' %}.
Example:
Before (template references non-existent layout):
{% layout 'fullwidth' %}
<h1>{{ page.title }}</h1>Fix option 1 -- create the layout:
layouts/
default.liquid
fullwidth.liquid <-- add this fileFix option 2 -- update the reference:
{% layout 'default' %}
<h1>{{ page.title }}</h1>Warnings
missing-schema-tag
Severity: Warning
Category: Schema Validation
Message: Template is missing a schema tag. / Block is missing a schema tag.
Cause: A .liquid file in templates/ or blocks/ does not contain a {% schema %} tag. While not strictly required for templates, a schema allows the CMS to generate editable fields and configure block allowlists.
Fix: Add a {% schema %} tag. Even if the template has no configurable settings, include an empty settings array.
Example:
{% schema %}
{
"settings": [],
"blocks": ["hero", "text-section"]
}
{% endschema %}See also:
multiple-schema-tags
Severity: Warning
Category: Schema Validation
Message: Block has multiple schema tags; only the first will be used. / Template has multiple schema tags; only the first will be used.
Cause: A .liquid file contains more than one {% schema %}...{% endschema %} block. The validator only parses the first one and ignores subsequent schema tags.
Fix: Remove all but one {% schema %} tag from the file. Consolidate settings into a single schema block.
Example:
Before:
{% schema %}
{ "name": "hero", "settings": [{ "name": "heading", "type": "text" }] }
{% endschema %}
{% schema %}
{ "name": "hero", "settings": [{ "name": "subheading", "type": "text" }] }
{% endschema %}After:
{% schema %}
{
"name": "hero",
"settings": [
{ "name": "heading", "type": "text" },
{ "name": "subheading", "type": "text" }
]
}
{% endschema %}orphaned-assets
Severity: Warning
Category: Asset Integrity
Message: Orphaned asset assets/<path>.
Cause: A file exists in the assets/ directory but is not referenced by any template, layout, or snippet via asset_url or a direct path. This increases the theme bundle size without benefit.
Fix: Remove unused asset files from the assets/ directory. If the asset is used dynamically (e.g. via a setting value), you can safely ignore this warning.
large-asset
Severity: Warning
Category: Best Practice / Asset Integrity
Message: Image exceeds recommended size (<size> bytes). / Asset exceeds recommended size (<size> bytes).
Cause: An asset file exceeds the recommended size limits:
- Images (gif, ico, jpg, jpeg, png, svg, webp): 1.5 MB
- Other assets (css, js, fonts, etc.): 2.5 MB
Fix: Optimise the file:
- Compress images using tools such as
sharp,imagemin, or an online compressor. - Convert images to modern formats like
.webp. - Minify CSS and JavaScript files.
- Subset web fonts to include only the required character ranges.
deprecated-include
Severity: Warning
Category: Compatibility
Message: Include tags are deprecated. Use render instead.
Cause: A template uses the {% include %} tag. This tag is deprecated in favour of {% render %}, which provides better encapsulation and does not leak variables into the parent scope.
Fix: Replace {% include %} with {% render %}. Note that {% render %} has different variable scoping -- pass variables explicitly.
Example:
Before:
{% include 'header' %}After:
{% render 'header', site: site, page: page %}missing-meta-description
Severity: Warning
Category: Best Practice
Message: No meta description tag found in templates or layouts.
Cause: No template or layout in the theme includes a <meta name="description"> tag. Search engines use this tag for indexing and display in search results.
Fix: Add a meta description tag to the layout or to individual templates.
Example:
<head>
<meta name="description" content="{{ page.meta_description | default: site.description }}">
</head>deep-nesting
Severity: Warning
Category: Best Practice
Message: Field nesting depth is <depth>. Consider simplifying complex groups or arrays.
Cause: A schema's field structure (using group or array types) exceeds 4 levels of nesting. Deeply nested schemas produce complex editor interfaces and can degrade the content editing experience.
Fix: Simplify the schema structure. Flatten nested groups where possible, or split deeply nested configurations into separate blocks.
Example:
Before (5 levels deep):
{
"name": "layout",
"type": "group",
"fields": [
{
"name": "section",
"type": "group",
"fields": [
{
"name": "column",
"type": "group",
"fields": [
{
"name": "items",
"type": "array",
"fields": [
{ "name": "label", "type": "text" }
]
}
]
}
]
}
]
}After (3 levels, flattened):
{
"name": "items",
"type": "array",
"fields": [
{ "name": "section_label", "type": "text" },
{ "name": "column_label", "type": "text" },
{ "name": "item_label", "type": "text" }
]
}Index
By severity
Errors
- missing-layouts -- Required
layouts/directory is missing - missing-default-layout --
layouts/default.liquidnot found - block-missing-schema -- Block file lacks a
{% schema %}tag - invalid-schema-json --
{% schema %}tag contains invalid JSON - unsupported-asset-type -- Asset uses a disallowed file extension
- missing-asset-reference -- Template references a non-existent asset
- invalid-layout-reference -- Template references a non-existent layout
Warnings
- block-duplicate-name -- Two or more blocks share the same name
- unsupported-field-type -- Schema field uses an unrecognised type
- missing-schema-tag -- Template or block file has no schema tag
- multiple-schema-tags -- File contains more than one schema tag
- orphaned-assets -- Assets not referenced by any template
- large-asset -- Asset exceeds recommended size limits
- deprecated-include -- Template uses deprecated
{% include %}tag - missing-meta-description -- No meta description tag found
- deep-nesting -- Field nesting exceeds 4 levels
By category
Schema Validation
- block-missing-schema
- block-duplicate-name
- invalid-schema-json
- unsupported-field-type
- missing-schema-tag
- multiple-schema-tags
Asset Integrity
Manifest Completeness
Compatibility
Best Practice
Related
- Common issues -- Diagnosing and fixing common theme development problems
- Theme structure -- Required directories and file conventions