Skip to content

FAQ and troubleshooting

Two parts. First the questions we get before anyone connects a first site. Then troubleshooting: every cause described here matches a real behaviour of the loader, the CLI or the admin, with the exact messages.

Your site keeps running. Graceful failure is not a marketing promise, it is structural:

  • The production site does not depend on us. It is a set of static files at your host: nothing in the serving path of a page goes through Menestrel.
  • Builds still pass. MENESTREL_CONTENT_URL reads the published snapshot from the CDN, not from the application. A redeploy during an outage, or after one, succeeds.
  • The full export is available on every plan, free included. npx menestrel export writes content/ as Markdown, data/ as verbatim JSON and media/ with the originals. No proprietary format, no support ticket.

After cancellation, content is kept for 30 days, with an export offered before deletion. The full mechanism is described in How Menestrel works.

Because depth of integration cannot be shared. The loader is a native Content Layer loader: entries come out typed, with ids designed for getStaticPaths, and the locale and translations exposed for your i18n routing. menestrel import reads your content collections directly. The components (RichText, CmsImage, CmsSeo) are Astro components.

A “works with every framework” CMS would have had to give all of that up. The two latest Astro majors are supported, see Astro compatibility. If your site is not on Astro, Menestrel is not for you, and we would rather say so.

In France. The database, the media and the snapshots are hosted at Scaleway, Paris region. Public distribution goes through a European CDN (Bunny). A DPA can be signed online, the sub-processor list is public, and the admin runs no third-party trackers. The details, retention periods included, are in API tokens and security.

Yes. Content is only needed at build time: the loader reads a published snapshot, astro build produces static files, and in production the site sends no request to Menestrel, neither on page load nor in the background. A traffic spike on your site is your static host’s business, never the CMS’s.

Free for one site (1 site, 2 editors, 200 entries, 2 locales). After that, €3/month per site billed yearly, or €5/month with no commitment. The price you sign up at is locked for life. The full plans are on menestrel.dev.

getCollection() returns [] while the admin shows content. Three real causes, in the order to check them:

  1. MENESTREL_OFFLINE=1 is still in the .env. It is the loader’s highest priority: even with MENESTREL_CONTENT_URL or MENESTREL_TOKEN set, it reads .menestrel/snapshot/, that is the starter’s demo fixture or an old pull. The starter sets this variable by default; menestrel init comments it out when it installs your token. Comment it out, or refresh the cache with npx menestrel pull.
  2. Nothing is published in that collection. A snapshot only contains published entries: a draft never appears in it. An entry not published in a locale is served with its fallback locale, then with the default locale; it only disappears from a locale when no locale in its fallback chain is published. Check the per-language status in the admin, then publish.
  3. The snapshot being read is not the latest. A pinned MENESTREL_SNAPSHOT_ID or a pull older than the publication serves you content that is exact, but stale.

If no source is configured (no URL, no token, no offline mode), there is no silent empty collection: the build fails with this message, escape hatch included.

[menestrel] No content source configured. Set MENESTREL_CONTENT_URL, or MENESTREL_TOKEN, or run `npx menestrel pull` and set MENESTREL_OFFLINE=1.

The message looks like [menestrel] GET https://... failed with HTTP 401. The two codes do not mean the same thing:

  • 401: the token is unknown, revoked or expired. The response never says which, on purpose: a stolen secret teaches nothing to whoever tests it. Create a new token in “Site settings”, “API tokens” tab, and replace the old one in the .env or at your host.
  • 403: the token exists but its scope is not enough. sync, import and export require an admin token (prefix cmt_adm_); for the loader at build time, read_published (cmt_pub_) is enough.

In production, the best move is still to set no token at all: with MENESTREL_CONTENT_URL, the build reads the CDN and can no longer hit a 401. See Environment variables.

Publishing does create a snapshot, but nothing rebuilds. Everything can be checked in “Site settings”, “Deployment” tab:

  • No host connected. Publishing materializes the snapshot, but nothing rebuilds until a host is wired up.
  • The target is “Paused”. After three consecutive trigger failures, Menestrel stops hammering the host and pauses the target. Fix the configuration (hook deleted on the host side, revoked token…), then click “Resume”: the failure counter starts over from zero.
  • The target is “Disabled”. It was switched off on purpose: “Enable” button.
  • It is not broken, it is batched. Each project has a build batching window (90 seconds by default, adjustable), plus an hourly trigger quota depending on the plan. Bursts of publications are grouped, never rejected.

The full cycle, deployment state machine included, is in Publishing and versions.

Run the same command again. menestrel import keeps local state in .menestrel/import-state.json, written atomically after each entry, and resuming never duplicates:

  • entries already created are skipped (counted as “skipped”), never recreated;
  • media files are deduplicated by SHA-256 checksum: nothing is re-uploaded;
  • a kill in the middle of a request is covered: if the server had already created the entry (slug already taken, singleton already present), the resume flags it (already_exists warning) instead of duplicating it.

A source file modified after the import is flagged (source_changed warning) but never re-imported: once the migration is done, the admin is the only source of truth. The full walkthrough is in Import an existing site.