Skip to content
Quilium Docs
Back to site
How-to guides

Troubleshooting

The failures that waste an afternoon, what actually causes them, and the check that tells them apart.

Most Quilium problems are one of a dozen, and almost none of them say what they are. This page maps the symptom to the cause.

Live examples. Every case below happened while building the demo sites used throughout this documentation, and the fix is the one that worked.

The page returns 404

Check If it is that
Is the page state: inactive? An inactive page 404s everywhere, locally included. "Draft" never means inactive. Set it back to active.
Does the route exist, and does its path start with a slash? A path without it glues to the page URL and matches nothing: {{article.slug}} on a page at /article gives /article<slug>.
Is it a detail page? Its page must exist, be hidden rather than deleted, and use the detail page-type.
Is it the 404 template itself failing? miscellaneous.rendering.404 must point at a template that exists, extension included.

The page renders empty, with no error

The template ran and produced nothing, which almost always means the data was not where you looked.

  • A collection item's fields are under data, a content block's under custom. Reading the wrong one returns blank, not an error.
  • A query's results live on the object that declares it: page.custom.<queryKey> for a page-type query, content.custom.<queryKey> for a block query.
  • route.* only exists inside a query config. In a template it silently resolves to nothing.

When it stays mysterious, probe instead of theorising:

<!--DBG articles=[{{ articles.size }}] first=[{{ articles[0].data.title }}]-->

Then curl <url> | grep DBG and delete the line. Some filters return empty without erroring, and guessing which one is slower than looking.

A write returns 422

Cause Fix
Wrong wrapper update-content writes under custom:, update-itemset-item under data:.
A reference sent as a string Images and files are arrays of objects, [{id: '<uuid>'}]; items and page references are flat arrays of ids, ['<uuid>']. Never a bare string.
A translation-only payload Send the main data plus one locale per call, never a translation alone.
UniqueUrl on publish Two pages resolve to the same URL, typically a detail child with an empty slug. Give it a real slug, or uncheck the parent prefix on the child.

A settings write silently does nothing

update-settings-partial requires target to be the top-level key of the YAML you send. A mismatch fails without a warning. And a replace sends the whole block: read it, merge, write it back entire, or you delete the fields you left out.

Translations do not show up

  • The interface string must be a literal: {{ 'Read more' | _ }} is extracted, {{ label | _ }} is not.
  • Extraction is a step, not magic. Run quilium dictionary parse (or quilium push, which runs it), then fill the translations.
  • A field that is not translatable: true in its type will never carry a second locale.

The live site does not match my machine

Push moves templates, publishing moves content. Doing one and expecting the other is the single most common surprise.

quilium push

Then publish the pages. See Publish and go live.

The CLI refuses to work

Symptom Cause and fix
quilium run says local files are missing You are in a folder with no theme. Run quilium pull.
pull refuses to run public/ is not empty. Back it up, or use --force, which replaces everything.
push refuses public/ is empty. Pushing an empty theme would blank the site.
A 403 mid-command Wrong account or expired session. quilium whoami answers it in one call.
Endpoints behave oddly Check quilium.json for a local override that travelled up on a push.

A block renders as a 500, or not at all

Symptom Cause and fix
ENOENT: Failed to lookup "elements/<key>.html" The content-type has no template: key, so the engine looked for <key>.html. Only .liquid loads. Add template: 'elements/<key>.liquid'.
The template: key is there and the error still names .html A content block freezes its template when it is created, exactly as a page freezes its page-type. Fixing the YAML does not reach blocks that already exist — delete the block and add it again.
A block silently loses its data halfway through a loop An {% include %} shares the caller's scope. A partial that does {% assign c = … %} overwrites the caller's c — and almost every element file opens on {% assign c = content.custom %}. Name partial-local variables after the partial. See Reuse blocks.
An external feed's block is empty, page returns 200 A failed api fetch yields an empty object, silently. See Consume an external feed.

The agent did something unexpected

  • It deactivated pages because it was told to keep them "in draft". Say "leave them unpublished", never "deactivate".
  • It filled a collection and the entries went live immediately. Entries have no draft state; only pages do. See Publish and go live.
  • It invented a structure because it never read the site. Make get-skills then get-site-settings the first two calls of the session, see Prompting well for Quilium.
  • It reported "done" without checking. Ask for the verification output, not the summary.

Related


Last updated September 7, 2026.