Skip to main content
View as Markdown

Validating a Config

sovrium validate decodes a config file against AppSchema — the same schema the server decodes at boot — and reports every problem it finds. It touches no database, binds no port, and needs no environment, which makes it the cheapest place to catch a broken config.

>_ terminal
sovrium validate app.yaml
sovrium validate config.json
sovrium validate app.ts

It accepts .json, .yaml, .yml and .ts, and resolves every $ref include before checking anything — so a config split across twenty files is validated as the one object it becomes.

What success looks like

code
Valid configuration: my-app

Exit code 0. This is the same decode the server performs at startup, in both directions: a config that validates will boot, and a config this rejects is one sovrium start and sovrium build refuse too.

What failure looks like

Problems print under a single Error: Validation failed. header, and exit 1. An unrecognised property is named, located, and answered with the keys that node does accept:

code
Error: Validation failed.

  Unknown property 'tag' on component type 'text'
    at pages[0].components[0]
    Accepted here: type, children, props, content, interactions, responsive,
                   visibility, i18n, session, element, required

When the spelling is a near miss, the report says so — Did you mean 'element'? — but only when the correction is actually derivable. A suggestion that is always produced would send you to the wrong property, so a name with no near neighbour gets the accepted-key list and nothing more.

Structural problems that are not a stray key — a missing name, a number where a string belongs — print as the decoder's indented tree instead. Read that one from the bottom: it walks down through the schema before it reaches your config, so the top is machinery and the last lines are the finding.

One property per run. A config with three typos reports one of them. Fix it, run again.

The four classes of error

Class Example Caught by
Structural name missing; a number given a string The AppSchema decode
Unrecognised property tabels: instead of tables: Excess-property rejection
Unknown field type type: web-site on a table field The post-decode sweep
Unresolvable field rowColorField: statuss on a table with no such field Cross-field checks

The second is the contract worth stating in full, because it governs the whole file:

Before this contract existed, an unrecognised key was dropped and the server started anyway — so tag: 'h1' on a text component (the property is spelled element) rendered a plain <p> with no error anywhere. The only evidence was the feature not being there.

That exception is also the escape hatch. An attribute Sovrium has no schema property for belongs under props:

app.yaml
components:
  - type: text
    content: Hello
    props:
      data-analytics-id: hero # forwarded verbatim

The third runs after the decode, and prints plainly rather than as a tree:

code
Error: Validation failed.

  Unknown field type "web-site" in field "website"

With a multi-file config the finding is attributed to the partial it came from — companies.yaml: Unknown field type ....

The fourth reads your config against itself: a component names a field, and the table it is bound to has to declare it.

code
Error: Validation failed.

  rowColorField: field 'statuss' not found in table 'orders'. Available: id, customer, status

This one changes what your app does, not only what the validator says. A columns[].field, a chart series[].field, a kanban colorField or a form fields[].field naming a column that is not there used to render nothing and report nothing — indistinguishable from a column whose rows happen to be empty. Listing a view type without the config that builds it behaved the same way: views: ['kanban'] with no kanbanGroupBy drew a tab that did nothing when clicked. Both are now refused by all three commands, so the mistake surfaces at your desk instead of shipping as a feature that looks deliberate.

System columns (id, the timestamps, the authorship columns) always resolve — they exist without appearing in fields[]. A component bound to a system source rather than a table is skipped, since its columns describe an endpoint's response rather than a declared table.

Exit codes

Exit code Meaning
0 Config is valid
1 Config is invalid (decode error, unknown field type, missing file)

Everything failed is 1, so gating a pipeline is a single line:

>_ terminal
sovrium validate app.yaml || exit 1

Run it before the deploy step. It is the last point where a bad config costs you seconds instead of a rollback.

Validating from inside a config

To check a config from a running app rather than from a shell — a webhook that receives a submitted config, a scheduled audit — use the sovrium:validateConfig automation action. It runs this same decoder, with no side effects and no boot, and exposes {{steps.<name>.valid}} and {{steps.<name>.errors}}:

app.yaml
automations:
  - name: check-submitted-config
    trigger:
      type: webhook
      method: POST
    actions:
      - name: check
        type: sovrium
        operator: validateConfig
        props:
          config: '{{trigger.data.config}}'
          format: auto

There is no looser in-process alternative, by design: a second validator that tolerated what this one rejects would mean a config could pass one gate and fail the other. The candidate is also read verbatim — a {{...}} or $env.X inside the submitted config is validated as literal text, never resolved — so the verdict describes what you passed rather than a rewritten copy of it. Full details on Validation & Schema Generation and Automation Actions.

Last updated September 1, 2026

This documentation was written with AI, so errors or outdated content are possible. Sovrium is in beta. Contributions and corrections are welcome.

Built with Sovrium