Skip to content

Theme validation

Every rule theme check applies with its verbatim error string, the limits, the remote check, the validator-lag note, and a CI recipe.

Updated 2 Sept 20264 min read
On this page

This page is the reference for anyone who met a ✗ line from theme check or from a push. The validator theme check runs locally is the same file the server runs at push, so every rule here applies verbatim in both places.

How it reports#

Text
✗ templates/home.liquid: <script> is not allowed (themes ship no JavaScript; JSON-LD data blocks are the only exception)
✗ disallowed file extension: assets/app.js

2 error(s) block pushing. Fix them, then run `dukkan theme check` again. https://developer.dukkan.one/docs/themes/check

On success:

Text
✓ Bundle valid — 14 files, hash 3f9a1c2b7d4e…

The command exits 1 on any error and 0 on success. Dotfiles, dot-directories and node_modules are skipped silently; symlinks are skipped with the warning ! skipped PATH (symlink).

Path rules#

Error Cause
bundle is empty no files
bundle has too many files (N) more than 400 files
path length out of range: "…" an empty path or one longer than 200 characters
absolute path not allowed: PATH starts with /
backslash not allowed: PATH contains \
NUL byte in path contains a NUL byte
path traversal / empty segment not allowed: PATH contains .., . or an empty segment
dotfiles not allowed: PATH a segment starts with a dot
disallowed file extension: PATH an extension outside .liquid .json .css .svg .png .jpg .jpeg .webp .woff2

Size rules#

Error Limit
file too large: PATH a file over 1 MB
bundle too large (N bytes) a total over 5 MB
theme exceeds 400 files / theme exceeds the total bundle size limit the CLI's directory reader stops early, before validation

Content rules#

.liquid and .css files are scanned as text:

Error What it catches
PATH: <script> is not allowed (themes ship no JavaScript; JSON-LD data blocks are the only exception) any <script> except exactly <script type="application/ld+json">
PATH: inline event handlers (onclick=...) are not allowed any on*= attribute
PATH: javascript: URLs are not allowed javascript:
PATH: data:text/html URLs are not allowed data:text/html
PATH: <iframe> is not allowed <iframe>
PATH: the \raw` filter is not allowed (breaks output escaping)` `
PATH: "<" is not allowed in CSS (prevents </style> breakout) any < inside a CSS file
PATH: section wrapper markers are platform-emitted and not allowed in theme code data-dukkan-section or data-section-id= in Liquid

Structure rules#

Error Cause
missing required template layout/theme.liquid no layout
missing required config/settings_schema.json no schema
config/settings_schema.json is not valid JSON invalid JSON
config/settings_schema.json: … a schema error; see Schema errors
snippets/sections.liquid is a reserved platform path a platform-reserved file exists in your bundle
theme declares sections but ships no snippets/section-body.liquid a schema with sections but no body dispatcher

Schema errors#

They appear prefixed with config/settings_schema.json::

Error Cause
path.to.key: message a structural violation (extra key, disallowed type, string over its limit, schema_version not 1)
duplicate group id "x" a repeated group id
duplicate setting id "x" in group "g" a repeated setting id
setting id "x" in group "g" is reserved pages, translations or a reserved object key used as a global id
duplicate section type "x" a repeated section type
duplicate block type "b" in section "s" a repeated block type
select "x" in … must declare options a select without options
setting "x" in … declares options but is not a select options on a non-select
setting "x" in … declares source but is not a picker source on a non-product/category
setting "x" in … declares localized but is not text localized on a non-text/textarea/richtext
setting "x" in … has min greater than max min > max
default … (where) a default that does not match its type

The full specification is in Settings schema.

The remote check#

Shell
npx @dukkan.one/theme-cli theme check --remote

It sends the bundle to POST /theme-api/v1/theme/check, which re-runs the rules above on the server and then performs a smoke render of templates/home.liquid in the same sandbox that gates publication, with a dummy store, one product and one instance of every section type you declare. A failure comes back as a render failed: <code> line, where <code> is one of:

Code Meaning
timeout the render exceeded 1.5 seconds of engine work or 3 seconds overall
render_error a Liquid error (unknown tag, unknown filter, include of a missing file)
output_cap output over 1 MB
template_missing templates/home.liquid is absent
worker_unavailable / busy server pressure; retry

At push the render output is checked too: a section wrapper without an id, an unknown id or a repeated id rejects the push even when the text scan passed.

Validator lag in the published release#

Warning

The published @dukkan.one/theme-cli@0.1.0 bundles a validator copy older than the current schema: it locally rejects band and newer additions the server accepts. If you see a schema error on a key documented here, the remote check is the judge. Until a newer release ships, either leave band off or run the CLI build from the platform repository.

In the dukkan-themes repository the variable DUKKAN_CLI=/path/to/app/cli/dist/index.js points the repo tooling at an unreleased CLI build; see the CLI reference.

CI recipe#

.github/workflows/theme-check.ymlYAML
name: theme-check
on: [push, pull_request]
jobs:
  check:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: 20
      - run: npx @dukkan.one/theme-cli@0.1.0 theme check .

The local check needs no sign-in; --remote needs a CLI token, so keep it for a manual run or a step that reads its credentials from a secret.