Skip to content

stories validate

Checks the content of every story in a Storyblok space against a local TypeScript schema. The command is read-only: it modifies nothing in the space.

Use it to find content that no longer fits the schema: a block removed from the code, a field renamed, a required field never filled in, or an option value that no longer exists.

Option values are only checked for fields whose options are defined in the schema itself (self-sourced). A field with a source (an internal datasource, internal_stories, internal_languages, or external) resolves its options inside the space, and a datasource definition carries no entries, because entries are content rather than schema. Those values are therefore not knowable from local code.

  • An entry file that exports at least one block, authored with @storyblok/schema. Story content cannot be validated against a schema that defines no blocks, so such an entry file is rejected with exit code 2 instead of reporting unknown_component on every story.
Terminal window
storyblok stories validate [flags]
Flag Type Description
--space, -s integer Required. The ID of the Storyblok space whose stories are validated.
--schema string Required. Path to the TypeScript schema entry file.
--starts-with string Optional. Only validate stories whose path starts with this prefix, for example en/blog/.
--level string Optional. Display threshold: error hides warnings, warning shows everything. Defaults to warning.
--format string Optional. Output format: pretty or json. Defaults to pretty.

--level only filters which issues are displayed. It never changes the exit code or the reported totals.

Folders are skipped, since they carry no content, and are excluded from the story total. When --starts-with matches no stories, the command says so rather than reporting a clean run over nothing.

The following examples assume that a space has been defined in a configuration file.

Terminal window
# Validate every story against the local schema
storyblok stories validate --schema src/schema/schema.ts
# Validate only the stories below a path prefix
storyblok stories validate --schema src/schema/schema.ts --starts-with "en/blog/"
# Fail only on errors and hide warnings
storyblok stories validate --schema src/schema/schema.ts --level error
# Emit a machine-readable report
storyblok stories validate --schema src/schema/schema.ts --format json > stories-report.json

Output:

home (story #12345)
✖ missing_required_field content.headline: Missing required field "headline" on component "page".
⚠ unknown_field content.legacy_cta: Unknown field "legacy_cta" on component "page".
✖ 1 error, 1 warning across 1 of 17 stories

Groups are sorted by story path, so the output of two runs over identical content is diffable.

With --format json, the command writes a single JSON object to stdout and nothing else, so it can be piped or redirected. All human-facing output (the title, progress, and errors) goes to stderr.

{
"ok": false,
"unit": "stories",
"unitsTotal": 17,
"unitsWithIssues": 1,
"errors": 1,
"warnings": 1,
"fetchFailures": 0,
"listFailed": false,
"groups": [
{
"header": "home (story #12345)",
"ref": { "kind": "story", "id": 12345, "slug": "home", "name": "Home" },
"issues": [
{
"severity": "error",
"code": "missing_required_field",
"path": ["content", "headline"],
"entity": "block:page",
"message": "Missing required field \"headline\" on component \"page\"."
}
]
}
]
}

ok is true only for a complete run with no errors. Warnings alone keep it true. But a failed listing (listFailed) or any story that could not be fetched (fetchFailures) makes it false, even with zero issues, so a CI consumer never reads success over an incomplete run. In those cases, listError and fetchErrors carry the reasons, which a consumer reading only stdout would otherwise never see.

Every group carries a ref with the machine-readable identity of the story the issues belong to. Use it instead of parsing header. The counts are always true totals, unaffected by --level.

Was this page helpful?

What went wrong?

This site uses reCAPTCHA and Google's Privacy Policy (opens in a new window).Terms of Service (opens in a new window) apply.