TOML Schema Specification
TOML Schema is a set of TOML-based constructs that define the structure, the names, and the types of configuration data in a TOML file.
TOML Schema validates the parsed input of a TOML file to:
- Eliminate or reduce misconfiguration that could cause damage if it were only detected when the configuration is evaluated in production,
- Be leveraged by editors and other tools to provide and enrich auto-completion and code hints for validation on the fly.
The schema format follows the TOML specification, meaning that a TOML Schema is in itself a valid TOML document. The revision of TOML that this specification is defined against is pinned under TOML Language Version.
Conformance Terminology
The key words MUST, MUST NOT, REQUIRED, SHOULD, SHOULD NOT, MAY, and OPTIONAL in this document are to be interpreted as described in BCP 14 when, and only when, they appear in all capitals.
A schema loader parses a TOML Schema document and rejects malformed schemas. A validator applies a successfully loaded schema to a TOML document. An implementation may provide both components in one API or command.
Terminology
This section defines the domain terms the normative rules in this document are written with. It complements Conformance Terminology: that section fixes the BCP 14 keywords and the schema loader, validator, and implementation roles, and this one fixes the nouns those roles act on. Where an authoritative rule for a concept already exists, the entry below points at the section that owns it rather than restating it.
Two sides are kept strictly apart, because most cross-term confusion comes from mixing them:
- the schema side — the TOML Schema document and the definitions it contains; and
- the document side — the parsed TOML document under validation and its values.
A term that names something on one side MUST NOT be read as its counterpart on the other.
Schema-Side Terms
Schema definition (also definition). One coherent set of rules that governs a single position. A definition is written as a TOML table or key/value entry inside
[types]or[elements], or nested below one. Every definition has exactly one effective type and MAY carry schema definition properties. "Definition" is the preferred short form; "schema definition" is the same thing spelled in full.Element. A definition that is a direct child of
[elements]; it describes one top-level key of the validated document. See Elements Table. Elements follow the same rules as[types]definitions except that an element MUST NOT reference another element.Schema (definition) property. A key/value pair written directly inside a definition whose name is one of the closed set of keywords listed under Schema Definition Properties (
type,oneof,itemtype,optional,default, and the rest). A schema property is metadata on the schema side; it is never a key of the validated document. A same-named document key is instead described by a child definition (see fixed child), written through the reservedchildrennamespace when the definition also uses the property. Do not use "property" for a document key.Selector. The property by which a definition chooses the type it governs:
type,oneof,anyof, or theif/then/elsetriple. At most one selector applies to a definition, and two constructs supply an omitted one: a definition with nested child definitions and no explicit selector is an implicit table (type = "table"), and a definition whose only applicator isallofis a pure mixin whose effective type comes from its components. See Selectors and Composition Supplying the Local Skeleton.Schema type. The type a definition selects through
type: a built-in type name ("string","integer","array","table","collection","any", a temporal type, and so on) or a named reusable definition from[types]. The schema type is what is written; it is not yet resolved through references or composition.Effective type. The type a definition resolves to after following every named
typereference to a concrete definition and composing everyallofcomponent. Compatibility of a composition, the applicability of kind-specific properties such asformat,min, andkeypattern, and the choice of merge rule are all decided from the effective type, not from the written schema type. A definition's effective type has a coarse category — a scalar of a specific scalar type,array,table, orcollection— and that category is what a composition MUST agree on and what a parsed value's TOML kind (below) is checked against. Earlier drafts of this document spelled this concept effective kind or effective TOML kind; those are the same concept, and effective type is the preferred term.Named type / reusable definition. A definition under
[types], referenced by name from a selector,itemtype,items, orallof. See Types Table.Type reference / reference. A string that names a built-in type or a named reusable definition, accepted by
type,itemtype,items,oneof,anyof,allof,then, andelse. The optionaltypes.prefix is stripped once before lookup. Which reference each of those positions accepts is stated under Type Reference Restrictions, and the resolution and edge-classification rules for references are defined under Reference Resolution and The Reference Graph.Use site. The location at which a named definition is referenced, as opposed to the named definition itself. A use-site annotation (
optional,default) is one written at the reference rather than on the referenced definition; the interaction of use-site and referenced annotations is defined under Type Reference, Optionality, and Default.Participant and
allofcomponent. In anallofcomposition, a participant is the local definition or any one of itsallofcomponents, as defined in place under Merging by TOML Kind. Anallofcomponent is one referenced definition contributed to the composition.Pure mixin. A definition whose only applicator is a non-empty
allof, with no selector and no nested child definition. It is valid when its components resolve to exactly one TOML kind, which becomes its effective type. Composition Supplying the Local Skeleton is authoritative.Per-member value-constraint subset. The five properties
allowedvalues,min,max,pattern, andformat, which MAY be written directly on anarrayor acollectionand then constrain each item or each dynamic entry rather than the container.minlengthandmaxlengthare not in the subset; on a container they bound its member count. Per-Member Value Constraints is authoritative.Local check and effective check. An applicability or exclusivity check that reads one definition only, versus one that reads the composed view contributed by
allofcomponents, alternatives, or branches. Checks are local unless a rule explicitly says effective, and Local and Effective Checks enumerates every effective one.Assertion, annotation, and applicator. An assertion is a schema property a document value must satisfy for the node to be valid. An annotation never decides validity;
description,default, anddeprecatedare the annotations, as defined under Annotations. An applicator applies other definitions to the same node without selecting its type;allofis the only applicator in version 1.0, as described under Conjunctive Composition.Alternative. One entry of a
oneoforanyofarray — a candidate type the value MAY validate against. See Alternative Types. Distinct from branch.Branch. One of the two named table-like definitions selected by an
if/then/elseconditional:thenwhen the condition holds,elseotherwise. See Conditional Selection. Only the selected branch is applied. Distinct from alternative: a branch belongs to a conditional, an alternative belongs to a union.Fixed child. A child that a definition names statically, as opposed to a dynamically keyed collection entry. The fixed children of a definition are its own nested child definitions — including those written through the reserved
childrennamespace — plus those contributed byallofcomponents and by the selected alternative or branch. Entries underchildrenare fixed children:childrenis a purely syntactic escape for target keys whose names collide with schema property names, not a different kind of child, and both spellings denote the same target key and validate identically (see Quoted and Special Keys). The operand of a sibling presence rule and of a conditionalkeyis a fixed child's target key name, never the literal stringchildren. Which fixed children count for which rule is governed by the two sets below.Determinate fixed-child set. The fixed children a definition is known to have whatever document value is later validated against it, computed at schema-load time without reference to any document.
oneof/anyofalternatives andif/then/elsebranches contribute nothing to it. It is the set every schema-load rule reads: sibling-rule operand resolution,exactlyoneapplicability, and the collectionitemtyperequirement. Determinate Fixed-Child Set is authoritative.Effective closure set. The fixed children in force for one particular document node, computed at validation time: the determinate fixed-child set of the node's definition plus the fixed children of whichever alternative or branch was selected for that node. It is the set that unknown-key rejection, requiredness, and per-child value validation read, and the set that decides whether a table is open or closed. Effective Closure Set is authoritative. The two sets MUST NOT be conflated: a schema-load rule MUST read the determinate set, and unknown-key rejection MUST read the effective closure set.
Effective definition. The single merged definition
allofproduces by combining the local definition with every component before any value is validated; the document value is validated against it as a whole. The Effective Definition is authoritative.
Document-Side Terms
Key. A decoded TOML key of the parsed document — the identifier of a table entry or the name of a top-level key. Keys are compared as decoded values, not by lexical spelling (see Parsed Value Equality). A direct child key is a key one level below the table currently being validated.
TOML kind (also parsed TOML kind). The kind of an actual parsed TOML value: a scalar of a specific type (string, integer, float, boolean, or one of the temporal types), an array, or a table.
collectionis not a TOML kind; it is a schema-level refinement of the table kind (see Container Types). Validation requires the parsed value's TOML kind to match the coarse category of the governing definition's effective type before any assertion is applied, as required by the kind check under Keyword Evaluation Order. "TOML kind" is a document-side term and MUST NOT be substituted for "effective type".Node (also document node). A value in the parsed document that a definition governs — a present scalar, array, or table value at some path. Validation is expressed as applying a definition to a node; The Validation Contract defines the
validate(node, definition)operation over this term. A node is always present: a position where no value exists is a slot, not a node.Slot. A position a definition governs, which MAY be absent. A slot is filled by a node when the corresponding value is present in the document and is empty when it is absent. This distinction is what makes requiredness and
optionalstatable: a required slot MUST be filled; an optional slot MAY be empty, and an annotation such as adefaultattaches to the slot even when no node fills it. Every node occupies a slot; not every slot holds a node.Collection entry / dynamic entry. A document key of a
collectionnode that is not a fixed child; its name is constrained bykeypatternand its value byitemtyperather than by a named child definition (see Collection of Elements for Dynamic Keys). A collection entry is never a sibling-rule operand.Open table / closed table. A document table node whose governing definition contributes no fixed child (open: any keys accepted, contents unvalidated) versus one that contributes at least one (closed: every key MUST be a fixed child). Openness is decided from the node's effective closure set; the authoritative rule is under Tables.
Instance path and schema path. The serialized location of a document node and of a schema location, respectively. Their grammars are defined under Instance Path and Schema Path.
Deprecated Near-Synonym Mapping
Some prose written before this section was added uses a near-synonym for one of the terms above. A later editorial pass SHOULD replace each deprecated spelling with its preferred term. Until then, the two spellings denote the same concept, and the preferred term is the one to use in new text.
| Deprecated / conflated spelling | Preferred term | Notes |
|---|---|---|
| schema node, current schema node | definition (current definition) | "node" is reserved for the document side |
| effective kind, effective TOML kind | effective type | one concept, three spellings; keep "effective type" |
branch (of a oneof/anyof) |
alternative | "branch" is reserved for if/then/else |
| property (of a document table) | key (or child) | "property" is reserved for a schema keyword |
| element (used for any definition) | definition | "element" is reserved for a direct child of [elements] |
| schema type (used for the resolved type) | effective type | "schema type" is the written selected type only |
| TOML kind (used for a definition's resolved type) | effective type | "TOML kind" is the parsed value's kind only |
component (bare, meaning an allof participant) |
participant / allof component |
see Merging by TOML Kind |
Table of Contents
- Conformance Terminology
- Terminology
- First Glance
- TOML Language Version
- Schema Structure Reference
- Metadata Table -
[toml-schema] - Elements Table -
[elements] - Types Table -
[types] - Schema Definitions
- Schema Definition Properties
- Quoted and Special Keys
- Selectors
- Annotations
- Constraints
- Container Types
- Conjunctive Composition -
allof
- Validation and Data Model
- Evaluation
- Diagnostics
- Schema Self-Validation
- Security Considerations
- Filename Extension
- MIME Type
- TOML Reference of a TOML Schema
First Glance
This section is informative. It illustrates the language through a worked pair of documents; the normative rules are stated in the sections that follow.
TOML Example
Let's look at the TOML example displayed on the front page of toml.io:
# This is a TOML document
title = "TOML Example"
[owner]
name = "Tom Preston-Werner"
dob = 1979-05-27T07:32:00-08:00
[database]
enabled = true
ports = [ 8000, 8001, 8002 ]
data = [ ["delta", "phi"], [3.14] ]
temp_targets = { cpu = 79.5, case = 72.0 }
[servers]
[servers.alpha]
ip = "10.0.0.1"
role = "frontend"
[servers.beta]
ip = "10.0.0.2"
role = "backend"
TOML Schema Example
Example of a TOML Schema that validates the TOML document above:
# This is a TOML Schema document
[toml-schema]
version = "1.0.0"
[types.serverType]
type="table"
[types.serverType.ip]
type="string"
format="ipv4"
[types.serverType.role]
type="string"
[elements.title]
type="string"
[elements.owner]
type="table"
[elements.owner.name]
type="string"
[elements.owner.dob]
type="offset-date-time"
[elements.database]
type="table"
[elements.database.enabled]
type = "boolean"
[elements.database.ports]
type = "array"
itemtype = "integer"
[elements.database.data]
type = "array"
itemtype = "array"
[elements.database.temp_targets]
type = "table"
[elements.servers]
type="collection"
itemtype = "types.serverType"
minlength = 1
TOML Language Version
This specification is defined against TOML 1.0.0.
That revision fixes both the grammar a conforming parser accepts and the logical
value model — the TOML kinds string, integer, float, boolean, offset
date-time, local date-time, local date, local time, array, and table — that every
rule in this document is written against.
- A TOML Schema document MUST be a well-formed TOML 1.0.0 document. Schema loaders MUST reject a schema document that is not.
- A TOML document being validated MUST be parsed as TOML 1.0.0 before validation
begins. Parsing precedes validation: a document that is not well-formed TOML
never reaches a validator, and its parse failure is a parse error rather than
a validation diagnostic. Because no validation occurred, an implementation
MUST NOT report that failure as a diagnostic under either a registry code or
an extension code, and MUST NOT report the document as invalid; a
command-line validator reports it as an unusable invocation and exits
2(see Command-Line Exit Status).
This baseline is a property of the language, not of an individual schema. No schema property selects a TOML version, and a schema document cannot request a different one.
A TOML parser MAY accept input beyond TOML 1.0.0, whether from a later TOML revision — multi-line inline tables, trailing commas in inline tables, additional string escapes, or omitted seconds in date-times, for example — or from a vendor extension. Such a parser MUST NOT silently change validation outcomes. An implementation built on one:
- MUST NOT report a schema document or a validated document as conforming to TOML Schema 1.0 when the document parses only because of an extension;
- MUST NOT let an extension change the logical value produced for input that is well-formed TOML 1.0.0, because every validation rule in this specification is evaluated against that logical value; and
- MUST document the extended profile it accepts and SHOULD provide a mode that restricts parsing to TOML 1.0.0.
An implementation that cannot restrict its parser MUST treat acceptance beyond TOML 1.0.0 as an implementation extension and MUST report it, so that a schema or document that depends on it is never mistaken for a portable one. Conformance suites MUST use only TOML 1.0.0 syntax.
This is not the schema language version. The TOML version pinned here and
the TOML Schema language version described under
Schema Versioning are different numbers with different
meanings, and conflating them is a misreading this specification explicitly
rejects. [toml-schema].version states which revision of the schema
vocabulary — the properties, built-in type names, and validation rules defined
by this document — a schema document is written against. This section states
which revision of TOML itself both the schema document and the validated
document are parsed as. The two version numbers move independently: a schema
document that declares version = "1.0.0" declares TOML Schema 1.0.0, not
TOML 1.0.0, and a future TOML Schema version would not by itself change the
pinned TOML version.
Schema Structure Reference
A TOML Schema file has the following structure:
# Metadata
[toml-schema]
# Types
[types]
# Elements
[elements]
Top-Level Structure Conditions
[toml-schema]: table with information and metadata of the schema.- REQUIRED
[types]: table with definitions of types to be reused in elements.- OPTIONAL
[elements]: table with the overall structure of the TOML document, its tables, properties, and conditions.- REQUIRED
Any other top-level table or key-value pair MUST NOT appear in a TOML Schema document.
Metadata Table - [toml-schema]
This table is reserved for metadata regarding the TOML Schema itself.
[toml-schema]
version = "1.0.0"
# custom = "value" # *NOT allowed
[toml-schema.meta]
<any> = <value> # allowed
...
[toml-schema.meta.subtable] # allowed
# [toml-schema.custom] # *NOT allowed
...
Supported Properties
version: the TOML Schema language version used by this schema document. Type: string.- REQUIRED.
meta: subtable reserved for any custom user-provided metadata. Type: table.- OPTIONAL.
Custom properties and tables MUST NOT appear directly under toml-schema; they
MAY appear only inside the toml-schema.meta table.
Schema Versioning
This section is about the TOML Schema language version, which is unrelated to the TOML language version pinned under TOML Language Version. A schema document declares the schema vocabulary it uses; it never declares which revision of TOML it is written in.
TOML Schema follows the same version-numbering policy as the TOML specification: schema language versions use Semantic Versioning.
The version property MUST be a string containing a complete Semantic
Versioning 2.0.0 value. The MAJOR.MINOR.PATCH core is required; a valid
pre-release suffix and build metadata are optional. The current TOML Schema
version is 1.0.0.
[toml-schema]
version = "1.0.0"
Schema loaders MUST reject schema documents whose version is missing, is not a string, or is not a valid SemVer value. Shorthand values such as "1" and "1.0" are invalid.
An implementation that supports TOML Schema version MAJOR.MINOR.PATCH MUST accept schema documents with the same major version and a minor version less than or equal to the implementation's supported minor version. Patch versions, pre-release identifiers, and build metadata do not add schema-language features and do not affect compatibility. Schema loaders MUST reject schema documents with an unsupported major version or a greater minor version. Support for schema documents from an earlier major version is implementation-defined; an implementation MUST NOT treat support for a later major version as implicit support for an earlier one.
That compatibility rule assumes a non-zero major version. Semantic Versioning
2.0.0 gives 0.y.z no stability guarantee and permits a minor increment to
change the language incompatibly, so the rule's "same major version" test would
not be sound there. This specification therefore defines no major-version-zero
language version: the first TOML Schema language version is 1.0.0. A 0.y.z
value is well-formed Semantic Versioning but is not a TOML Schema language
version, and a schema loader MUST reject it as an unsupported major version.
Elements Table - [elements]
The [elements] table is the root schema for the TOML document being validated. Schema authors use it to describe the application data that may appear at the top level of the TOML document.
Each direct child of [elements] defines one top-level TOML key. Nested children define the structure below that key, such as fields inside a table or item definitions inside arrays and collections.
The root is closed: every top-level application-data key MUST be defined by a direct child of [elements]. Validators MUST reject any other top-level application-data key. This rule applies even when [elements] has no children, so an empty [elements] table accepts no application data.
The reserved root [toml-schema] metadata table is the only exception. When [elements.toml-schema] is omitted, validators ignore that table during application-data validation as described in TOML Reference of a TOML Schema. Therefore, a document validated by an empty [elements] table may contain only the reserved [toml-schema] table and no application data.
This root behavior differs from a nested property declared as type = "table" with no child definitions. Such a nested table is intentionally open-ended as described in Tables; an empty [elements] table is not an implicit allow-anything schema.
For example, this schema:
[elements.title]
type = "string"
[elements.database]
type = "table"
[elements.database.enabled]
type = "boolean"
validates a TOML document shaped like this:
title = "Example"
[database]
enabled = true
Use [elements] for document-specific keys. Use [types] for reusable definitions that can be referenced from [elements] or from other reusable types. Elements follow the same structure and validation rules as types, except that elements cannot reference other elements. To reuse conditions and structures, define them under [types] and reference them from [elements].
Types Table - [types]
The [types] table is for use when there is a need for custom, reusable types of structure or properties. A type is referenced in an element or another type with a type reference.
Type references are strings accepted by type, itemtype, items, oneof,
anyof, allof, then, and else. A type reference may be either:
- a built-in type name such as
"string","boolean", or"integer"; - a named reusable definition from
[types], written either as"types.<typename>"or"<typename>".
Which references each of those positions accepts, and which recursion each permits, is stated once under Type Reference Restrictions.
Each reusable type is a direct child of [types], and its name is the exact
decoded TOML key of that child. A dot in a type name is an ordinary character
when the key is quoted, so [types."network.endpoint"] defines the reusable
type named network.endpoint; it does not define a nested type hierarchy.
Tables below a direct child define that type's fixed children.
The optional types. reference prefix is removed exactly once before lookup.
Consequently, both "network.endpoint" and "types.network.endpoint" refer
to the direct definition [types."network.endpoint"]. To keep those two forms
unambiguous, a reusable type name MUST NOT begin with the literal characters
types.. Built-in type names are also reserved and MUST NOT be used as
[types] definition names. The reserved names are any, string, integer,
float, boolean, offset-date-time, local-date-time, local-date,
local-time, array, table, and collection.
Schema Definitions
Schema Definition Properties
The following matrix summarizes where definition properties apply. The detailed sections below remain authoritative.
| Property | Applicable definition |
|---|---|
type |
Selects one built-in or named type; at most one selector per definition, per Selectors |
oneof, anyof |
Select the current node from one or more alternatives; at most one selector per definition, per Selectors |
if, then, else |
Exhaustively select one of two named table-like definitions from a direct child's parsed value |
allof |
Conjunctively applies one or more compatible type references in addition to the local definition; alone, it also supplies an omitted selector, per Composition Supplying the Local Skeleton |
description, optional, default, deprecated |
Any definition, including a named reference or alternative selector |
format |
A definition with built-in type = "string"; or, as a per-member constraint, a non-tuple array or collection whose member type is string |
itemtype |
A definition with built-in type = "array" or type = "collection" |
items |
A definition with built-in type = "array"; mutually exclusive with itemtype, minlength, maxlength, and the whole per-member value-constraint subset (allowedvalues, min, max, pattern, format) |
allowedvalues |
A scalar or unconstrained built-in type; or, as a per-member constraint, a non-tuple array or collection |
pattern |
A definition with built-in type = "string"; or, as a per-member constraint, a non-tuple array or collection whose member type is string |
keypattern |
A definition with built-in type = "collection"; constrains dynamic entry keys, never their values |
min, max |
A numeric or temporal built-in type; or, as a per-member constraint, a non-tuple array or collection whose member type resolves to one comparable kind |
minlength, maxlength |
A definition with built-in type = "string", type = "array", or type = "collection"; on a container these always bound the container's own member count and are never per-member |
uniqueitems |
A definition with built-in type = "array" |
dependentrequired, mutuallyexclusive, exactlyone |
A definition with effective type table or collection and a non-empty determinate fixed-child set |
Every applicability and exclusivity statement in this matrix is local unless it names the effective type or an effective set, as Local and Effective Checks requires.
A named type reference, alternative selector, and conditional selector may
additionally declare only allof, description, optional, default, and
deprecated.
Kind-specific constraints for the referenced or alternative types belong in
reusable definitions.
The properties named in the matrix above are the complete set of schema
definition properties defined by this version of the language. In full, they
are type, description, format, itemtype, items, oneof, anyof,
if, then, else, allof, allowedvalues, pattern, keypattern,
optional, min, max, minlength, maxlength, uniqueitems,
dependentrequired, mutuallyexclusive, exactlyone, default, and
deprecated. The set is closed: a key/value pair written directly inside a
schema definition whose name is not one of those properties is malformed, and
schema loaders MUST reject it at schema-load time rather than ignoring it. A
misspelled property such as patttern is therefore an error and never a
silently inert annotation. Applying a recognized property to a definition that
the matrix above and the detailed sections do not permit it on is likewise a
schema-load error. Custom, tool-specific, or experimental keys have no place in
a schema definition; they belong in the [toml-schema.meta] table described
under Supported Properties.
The set is closed per language version rather than permanently fixed. A future MINOR version of TOML Schema MAY add properties to it. Because an implementation MUST reject a schema document whose minor version is greater than the version it supports, as required by Schema Versioning, a loader never encounters a property introduced after the version it implements in a document it accepts. Within the versions an implementation does accept, the recognized set is exactly the set defined by this specification, so an unrecognized property name is always an error and never a forward-compatible extension point.
This closure constrains schema vocabulary only, never the key names a validated
TOML document may contain. As described under
Quoted and Special Keys, a key/value pair directly
inside a schema definition is a schema property, while a table-header path
segment below that definition is a child definition. A target document key named
pattern, min, or deprecated is described by a child definition of that
name, written through the reserved children namespace when the same definition
also uses the property and TOML cannot represent both spellings. Rejecting an
unrecognized property name therefore never restricts the application data a
schema can describe.
Local and Effective Checks
Applicability and exclusivity checks are local unless a rule in this specification explicitly says effective. This statement is authoritative for both readings, and the sections that depend on either one point here rather than restating it.
A local check reads one definition only: its own selector, its own key/value
properties, its own nested child definitions, and the definitions its own
references resolve to. It MUST NOT read the properties, fixed children,
itemtype, or keypattern contributed by an allof component, by a oneof or
anyof alternative, or by a conditional branch.
Locality is what makes these checks decidable one definition at a time, and it
is why an exclusion such as items against the
per-member value-constraint subset binds only
the definition that writes both. An allof component MAY contribute a
constraint the local definition could not have written beside items. The two
then apply conjunctively, as Merging by TOML Kind
requires, and the result is an effective definition that no document value
satisfies rather than a schema-load error. This is not a loophole in the
exclusion: an exclusion states what one definition may write, while composition
states what a value must satisfy, and the two questions have different answers
by design.
The checks that read the effective, composed view instead are exactly the following, and each says so where it is defined:
- Composition compatibility. Whether the participants of an
allofagree on one effective type, including the rejection of a multi-kind local union combined withallof. See The Effective Definition. - The effective type of a pure mixin. A definition whose only applicator is
alloftakes its effective type from its components. See Composition Supplying the Local Skeleton. - The collection
itemtyperequirement. Acollectionobtains its mandatory dynamic-entry rule locally or from a compatibleallofcomponent. See Collection of Elements for Dynamic Keys. - Sibling-rule operands and applicability.
dependentrequired,mutuallyexclusive, andexactlyoneresolve their operands against the determinate fixed-child set, which includes the children contributed byallof, and theirtable-or-collectionapplicability is decided from the effective type. See Sibling Presence Rules. defaultvalidity. A declareddefaultis checked against the full effective definition. See Default.- Openness, closure, requiredness, and unknown keys. Whether a table is open or closed, which document keys are unknown, which fixed children are required, and which definitions validate a present child are all decided from the node's effective closure set. See Tables.
- Conjunctive application of contributed assertions. Every assertion any
participant contributes applies to the document value, including an
itemtypeorkeypatterncontributed to a collection. See Merging by TOML Kind.
Items 1 through 5 are schema-load checks and read only what is determinate at schema-load time. Items 6 and 7 are document-validation checks. No other rule in this specification reads the composed view, and an implementation MUST NOT extend the effective reading to a check that is not listed here.
Quoted and Special Keys
Schema child definitions use TOML tables. When a target TOML key is empty or contains characters that TOML requires to be quoted, such as a literal dot, quote that key in the schema table path.
Target keys may have the same names as TOML Schema properties, such as type,
itemtype, optional, or pattern. A key/value pair directly inside a schema
definition is a schema property, while a table-header path segment below that
definition is normally a target child definition.
A schema definition with nested child definitions and no explicit selector is an implicit table, as Selectors defines. This lets schemas describe target keys that would otherwise share a name with schema properties when the parent does not also use the property.
TOML itself forbids one table from containing both a value and a subtable with
the same key. When a definition must simultaneously use a schema property and
define a target child with that property's name, place the child definition
under the reserved children table. The segment immediately following
children is the target key; children is not part of the target document
path.
The children table is a selective escape hatch, not a general alternative
child syntax. The restriction is categorical rather than per-definition: every
direct entry below children MUST be named either children or one of the
schema-definition properties listed above, and
schema loaders MUST reject any other name there. A loader does not additionally
require that the surrounding definition actually declare a property of that
name. Ordinary, quoted, dotted, and empty child keys continue to use direct TOML
table paths.
Consequently, when a definition does not declare the property in question, a
child whose target key is a property name has two valid spellings: the direct
table path and the children path. Both denote the same target key and validate
identically. Authors SHOULD prefer the direct form and reserve children for
definitions in which TOML makes the direct form impossible; a schema generator
MAY instead emit the children form uniformly for property-named children. When
a definition does declare that property as a key/value pair, TOML permits only
the children form, so exactly one spelling is available.
A table named children whose own definition declares a type, oneof,
anyof, or if selector property is an ordinary target child definition
rather than the escape namespace. This preserves the direct form for a target
child literally named children. A selectorless table child with that name is
written through the escape namespace as
[elements.parent.children.children].
For example, this schema defines a scalar target key named children directly:
[elements.parent]
type = "table"
[elements.parent.children]
type = "string"
It validates:
[parent]
children = "value"
The type selector makes [elements.parent.children] an ordinary child
definition. To define a selectorless target table named children, repeat the
name through the escape namespace:
[elements.parent]
type = "table"
[elements.parent.children.children]
[elements.parent.children.children.name]
type = "string"
It validates:
[parent.children]
name = "value"
The first children segment is the escape namespace. The second is the literal
target key, and the selectorless definition is inferred as type = "table".
Example TOML document:
[plugin]
type = "npm"
name = "example"
Schema:
[elements.plugin]
type = "table"
[elements.plugin.children.type]
type = "string"
allowedvalues = [ "npm", "local" ]
[elements.plugin.name]
type = "string"
Here elements.plugin.type = "table" selects the parent type, while
[elements.plugin.children.type] describes the target key plugin.type.
Without the children segment, TOML would require type to be both a string
and a table at the same path.
Selectors
type, oneof, anyof, and the if/then/else triple are alternative
ways to select the type of the current definition. A definition MUST declare
at most one selector, and two constructs supply the selection in place of one:
- a definition with nested child definitions MAY omit all selectors and is then
treated as
type = "table"; and - a definition whose only applicator is a non-empty
allofMAY omit all selectors and all nested child definitions, and then takes its effective type from that composition. Composition Supplying the Local Skeleton is authoritative for when such a definition is valid and for the code a loader reports when it is not.
Schema loaders MUST reject a definition that combines selectors, contains only
part of a conditional triple, or declares no selector, no nested child
definitions, and no allof. type accepts either a built-in type name or a
named reusable definition from [types]. Container member types are selected separately with
itemtype: it validates each member of an array or each dynamically keyed
value of a collection. itemtype requires the same definition to declare the
built-in type = "array" or type = "collection"; it cannot be attached to
another built-in or to a named type reference.
Nested child definitions are valid only when the current node selects the
built-in table or collection type, or when the node omits a selector and is
therefore an implicit table. Schema loaders MUST reject child definitions attached to
a scalar, array, named type reference, oneof, anyof, or conditional node rather than
silently ignoring them.
[types]
[types.<typename>]
type = "<type-reference>"
description = "<human-readable description>"
format = "<email|uuid|uri|hostname|ipv4|ipv6>"
itemtype = "<type-reference>"
items = [ "<type-reference>", ... ]
oneof = [ "<type-reference>", ... ]
anyof = [ "<type-reference>", ... ]
if = { key = "<direct-child-name>", equals = <toml-value> }
# or: if = { key = "<direct-child-name>", in = [ <toml-value>, ... ] }
then = "types.<typename>"
else = "types.<typename>"
allof = [ "<type-reference>", ... ]
allowedvalues = [ <array-with-enumeration-of-allowed-values> ]
pattern = "<string-regex-for-string-validation>"
keypattern = "<string-regex-for-collection-key-validation>"
optional = true|false
min = <integer | float | offset-date-time | local-date-time | local-date | local-time>
max = <integer | float | offset-date-time | local-date-time | local-date | local-time>
minlength = <integer>
maxlength = <integer>
uniqueitems = true|false
dependentrequired = { <fixed-child> = [ "<fixed-child>", ... ], ... }
mutuallyexclusive = [ [ "<fixed-child>", "<fixed-child>", ... ], ... ]
exactlyone = [ [ "<fixed-child>", "<fixed-child>", ... ], ... ]
default = <toml-value>
deprecated = true|false
Scalar and Unconstrained Built-in Types
The unconstrained built-in type is:
- Any:
any
The scalar built-in types are:
- String:
string - Integer:
integer - Float:
float - Boolean:
boolean - Offset Date-Time:
offset-date-time - Local Date-Time:
local-date-time - Local Date:
local-date - Local Time:
local-time
Type Reference
A type reference applies a built-in type or inherits the rules of a named reusable type. Both [types] definitions and [elements] definitions may use type references. The type property selects the current node's type; built-in and named references use the same syntax.
When type selects a named reusable definition, the reference inherits that
definition's validation rules as-is. This inheritance includes optional, as
Optionality defines. In version 1.0, the
referencing definition MAY additionally declare only allof, description,
optional, default, and deprecated; it MUST NOT declare any other sibling
property or child definition. allof adds conjunctive components rather than
overriding the named reference. A local default is the use-site annotation
defined under Default, and deprecated = false cannot
cancel deprecation inherited from a reference. In particular, validation
constraints such as pattern,
keypattern, min, max, minlength, maxlength, allowedvalues,
itemtype, and items cannot be added or overridden at the reference site.
Schema loaders MUST reject such schemas at schema-load time.
To specialize validation rules, declare another named reusable definition rather than adding constraints to a reference:
[types.lowercaseName]
type = "string"
pattern = "^[a-z]+$"
[elements.name]
type = "types.lowercaseName"
description = "Display name"
optional = true
The following is invalid because pattern attempts to override the referenced definition:
[types.name]
type = "string"
pattern = "^[a-z]+$"
[elements.name]
type = "types.name"
pattern = "^[A-Z]+$" # invalid
[types]
[types.nameType]
type="string"
pattern="[a-zA-Z]" # unanchored: matches any string containing a letter
[types.serverType.name]
type = "types.nameType"
[types.serverType.enabled]
type = "boolean"
[elements]
[elements.datacenter]
type="table"
[elements.datacenter.name]
type="types.nameType"
[elements.datacenter.tags]
type = "array"
itemtype = "string"
[elements.datacenter.servers]
type = "collection"
itemtype = "types.serverType"
Type Reference Restrictions
This section is the single authority for which references each reference position accepts. Every other mention of these restrictions in this specification points here rather than restating them.
| Position | Bare built-in name | Named [types] reference |
Reference to a pure mixin | Reference edge |
|---|---|---|---|---|
type |
Any built-in. collection only when the effective definition obtains an itemtype locally or from a compatible allof component |
Yes | Yes | Non-consuming |
itemtype |
Any built-in except collection |
Yes | Yes | Consuming |
items |
Any built-in except collection |
Yes | Yes | Consuming |
oneof, anyof |
Any built-in except any and collection |
Yes | Yes | Non-consuming |
allof |
Any built-in except any and collection |
Yes | Yes | Non-consuming |
then, else |
None; a bare built-in reference is invalid in either | Yes, and both branches MUST resolve to the same effective type, which MUST be table or collection |
Yes, subject to that same kind requirement | Non-consuming |
The two restricted built-ins are restricted for one reason each:
collectionis valid fortypeonly when the effective definition obtains anitemtypelocally or from a compatibleallofcomponent. It MUST NOT be used as a bare reference initemtype,items,oneof,anyof,allof,then, orelse, because those locations cannot supply the collection's dynamic-value rule. Schema loaders MUST reject such references at schema-load time.anyis valid fortype,itemtype, anditems, but it MUST NOT appear directly inoneof,anyof,allof,then, orelse. Schema loaders MUST reject a directanycomponent at schema-load time.
These restrictions apply to bare built-in references, not to named reusable
definitions. A named definition that declares a complete collection or selects
type = "any" remains a valid reference. A
pure mixin is an ordinary named
definition once its components determine its effective type, so it may be
referenced wherever any other named definition may be, subject to the same kind
requirement the position imposes on a named reference. What a definition may
declare beside a named reference is stated under
Type Reference.
Every named reference used by type, itemtype, items, oneof, anyof,
allof, then, or else
MUST resolve to a definition in [types]. Schema loaders MUST reject unresolved
references at schema-load time, including references in definitions that are
optional or not exercised by the document being validated. The ordered algorithm
that turns a reference string into a built-in type or a reusable definition is
defined under Reference Resolution, which is
authoritative for how the restrictions above are applied. An element MUST NOT
reference another element, as
Elements Table requires.
Type-selection and composition references MUST be acyclic. A cycle composed of
named type aliases, oneof alternatives, anyof alternatives, conditional
branches, or allof components — the positions the table above marks
non-consuming — cannot resolve to a concrete definition and
schema loaders MUST reject it at schema-load time. Structural recursion through
table or collection children, array itemtype, or tuple items — the
consuming positions — remains valid
because each recursive step consumes a nested document value.
Cycle Legality states this classification in terms of the
reference graph and is authoritative for deciding which cycles are legal.
Alternative Types - oneof and anyof
Use oneof or anyof when a value may validate against alternative type references.
oneof: exactly one referenced type must validate.anyof: at least one referenced type must validate.
These properties can be used anywhere a schema definition can appear, including an [elements] field, a reusable [types] definition, and a type referenced through itemtype for array or collection items. Which references an alternative may name is stated under
Type Reference Restrictions; use a named
reusable definition when an alternative needs a fully defined collection, an
intentionally unconstrained named branch, or any other constraint.
type, oneof, anyof, and the conditional triple all select the current
node's type, and a definition declares at most one of them, as
Selectors requires.
The array assigned to oneof or anyof MUST contain at least one type
reference. A union definition MAY additionally declare only description,
optional, default, deprecated, and allof; it MUST NOT declare another
validation property or any nested child definition. Schema loaders MUST reject
empty unions and other union siblings at schema-load time. Constraints required
by an alternative belong in a named reusable definition referenced by the
union.
The alternatives of a oneof or anyof array MUST be distinct. A repeated
alternative adds no branch and, for oneof, cannot be the exactly-one match.
Duplication is judged on resolved identity, after the optional types. prefix
has been removed, so oneof = [ "types.stringId", "stringId" ] names the same
definition twice and is malformed. Schema loaders MUST reject a duplicate
alternative at schema-load time. This differs from
items, where an entry denotes a
tuple position rather than an alternative and repetition is meaningful.
A union contributes nothing to the
determinate fixed-child set of the node it
selects, because no alternative is chosen until a document value exists. It does
contribute at validation time: when an alternative is evaluated against a
document node, that alternative's fixed children join the node's
effective closure set for that evaluation, so a key
the alternative declares is a known key of the node and MUST NOT be reported as
unexpected. This is what lets a union be composed into a table with allof
without the alternatives' own children becoming unknown keys.
When alternatives contain annotations, only successful alternatives contribute
them, and only for a value that is present. Deprecation warnings follow the
successful-alternative rule: oneof reports the warning of its single
successful alternative, and anyof reports the warnings of every successful
alternative, deduplicated as Diagnostics requires. Consequently,
a deprecated successful alternative contributes a warning even when another
successful anyof alternative is not deprecated; this preserves all annotations
attached to definitions that accepted the value. Alternative default
annotations are governed by Default: they are never
combined into the slot's effective default and never cause a schema-load
conflict. For a present value, an implementation MAY surface the default of each
successful alternative as a hint, deduplicated by
Parsed Value Equality. An alternative that fails
validation contributes neither defaults nor deprecation warnings; what happens to
its diagnostics is defined under
Alternative and Branch Commit and Discard.
Examples:
[types.stringId]
type = "string"
pattern = "^[a-z]+$"
[types.integerId]
type = "integer"
min = 1
[elements.id]
anyof = [ "types.stringId", "types.integerId" ]
[elements.simpleId]
oneof = [ "string", "integer" ]
Use a named reusable definition whenever an alternative needs constraints such as pattern, min, allowedvalues, itemtype, or child fields.
[types.dependencyVersion]
type = "string"
pattern = "^[0-9]+\\.[0-9]+\\.[0-9]+$"
[types.inlineDependency]
type = "table"
[types.dependency]
oneof = [ "types.dependencyVersion", "types.inlineDependency" ]
For container items with alternative types, use a named wrapper:
[types.dnsValue]
oneof = [ "types.ipAddress", "types.hostname" ]
[elements.dns]
type = "collection"
itemtype = "types.dnsValue"
A named container definition can also be an alternative. This models formats that accept either one table or an array of the same table shape:
[types.cascadeEntry]
type = "table"
[types.cascadeEntry.params]
type = "table"
optional = true
[types.cascadeEntries]
type = "array"
itemtype = "types.cascadeEntry"
[elements.cascade]
oneof = [ "types.cascadeEntry", "types.cascadeEntries" ]
Conditional Selection - if, then, and else
The if, then, and else properties form one exhaustive selector for a
table-like node. The condition inspects one direct child of the current parsed
table and selects one of two named reusable definitions:
[types.sqliteDatabase]
type = "table"
[types.sqliteDatabase.engine]
type = "string"
allowedvalues = [ "sqlite" ]
[types.sqliteDatabase.path]
type = "string"
[types.serverDatabase]
type = "table"
[types.serverDatabase.engine]
type = "string"
allowedvalues = [ "postgresql", "mysql" ]
[types.serverDatabase.host]
type = "string"
[types.database]
if = { key = "engine", equals = "sqlite" }
then = "types.sqliteDatabase"
else = "types.serverDatabase"
Both branches declare engine, the key the condition reads. That is required
rather than stylistic: see
The Discriminator Key and Closed Branches
below.
if MUST be an inline table containing key and exactly one of equals or
in. It MUST contain no other members.
Because if is also a legal child key, an if = { ... } key/value entry is
always the conditional selector, while a table header such as
[types.example.if] is always a child definition named if. Consequently the
condition MUST use inline-table syntax and can never be written as
[types.example.if]. A definition that needs a target child named if writes
that child through the children namespace, as described under
Quoted and Special Keys. A conditional definition
has no nested child definitions of its own, so the two spellings never compete
within one conditional definition, but a loader still MUST NOT infer the
selector from a table-form if.
keyMUST be a string naming one direct child key of the parsed table being validated. It is a decoded TOML key, not a dotted document path, and it does not have to name a schema-declared child, because a conditional definition has no nested child definitions of its own. An empty or literal dotted key is permitted.equalsaccepts one TOML value. The condition succeeds when the child exists and is equal to that value according to Parsed Value Equality.inMUST be a non-empty array. The condition succeeds when the child exists and is equal to at least one array entry according to Parsed Value Equality.
If the named child is absent, the condition is false. A validator MUST apply
only the selected branch: then when the condition is true and else when it
is false. The condition itself emits no document-validation diagnostic.
Validation diagnostics and deprecation annotations come only from the selected
branch. A branch default does not become the conditional slot's effective
default; for a present value, an implementation MAY surface the selected
branch's default as a hint.
The condition reads a direct child of a parsed table. When the document value at
a conditional node is not a table — a scalar, an array, or an array of tables —
no direct child can be read, so the condition cannot be satisfied and is false,
exactly as it is for an absent child. else is therefore the selected branch.
Because both branches MUST resolve to the same table-like effective type, that
value cannot validate against either branch, and the node is invalid. A
validator MUST report this as a kind mismatch against the common branch kind at
the node's location, using the type-mismatch code defined under
Validation Codes, and MUST NOT report the branch's internal
missing-required or unknown-key diagnostics for a value that is not a table
at all. It MUST NOT report a diagnostic describing the condition, which never
fails on its own.
Consequently, a default on a conditional definition MUST be a table. A
non-table default cannot satisfy either branch, so a schema loader MUST reject
it at schema-load time under the default-validation rule stated below.
Like a union, the conditional triple contributes nothing to the determinate fixed-child set of the node it selects. The selected branch's fixed children join the node's effective closure set at validation time, so the keys the matched branch declares are known keys of that node, while keys declared only by the branch that was not selected are not.
then and else MUST each be a string naming a reusable definition in
[types], and both definitions MUST resolve
to the same effective type, which MUST be table or collection, as
Type Reference Restrictions states.
Schema loaders MUST reject unresolved branches, different branch kinds,
branches that do not resolve to a table-like kind, and cycles through
conditional branches.
The conditional triple is one of the four selectors a definition declares at
most one of, as Selectors requires. A conditional
definition MAY additionally declare only allof,
description, optional, default, and deprecated; it MUST NOT contain
kind-specific validation properties or nested child definitions. An allof
component MUST be compatible with the common branch kind and is applied
conjunctively with whichever branch is selected.
Optionality belongs to the conditional definition, as Optionality defines. A default on the conditional definition MUST validate against the branch selected by that default at schema-load time.
Example with a multi-value condition:
[types.database]
if = { key = "engine", in = [ "postgresql", "mysql" ] }
then = "types.serverDatabase"
else = "types.embeddedDatabase"
Additional alternatives can be expressed by referencing another conditional
definition from then or else.
The Discriminator Key and Closed Branches
The key named by if.key is ordinary application data. It is read to select a
branch, and it is then validated by the selected branch like any other key. A
conditional definition contributes no fixed children of its own, so nothing
declares the discriminator on the node's behalf.
A branch that declares fixed children is closed against exactly those children, as Effective Closure Set defines. If such a branch omits the discriminator, the key the condition just read is not in the node's effective closure set and a validator MUST report it as an unknown key, so the conditional can never accept a document. Every closed branch MUST therefore declare the discriminator itself.
Schema loaders MUST reject a conditional definition when a branch has a
non-empty determinate fixed-child set that does
not contain the key named by if.key. That set is computed at schema-load time,
so this check catches the common authoring mistake without resolving
document-dependent shapes. A branch whose fixed children are contributed only by
a nested union or conditional has an empty determinate set, and a loader cannot
decide the question for it; the same unknown-key rule still applies at
validation time, and authors remain responsible for declaring the discriminator
in every shape a branch can take.
Two branch shapes need no declaration and are not rejected: a branch that is an
open table, whose effective closure set is empty and which therefore accepts the
discriminator as arbitrary data, and a branch that is a collection, whose
dynamic-entry rules accept the discriminator when its keypattern and
itemtype permit it.
The else branch is subject to the same rule as then. An else branch is
selected both when the discriminator holds a different value and when it is
absent, so a closed else branch typically declares the discriminator with
optional = true, with allowedvalues excluding the value that selects then,
or with both. In the example above, types.serverDatabase declares engine
with allowedvalues = [ "postgresql", "mysql" ], which both admits the key and
rejects a value that should have selected the other branch.
Annotations
description, default, and deprecated are annotations. Unlike the
assertions defined below, an annotation never decides validity: it cannot make a
document valid or invalid, and removing every annotation from a schema leaves
the set of documents that schema accepts unchanged. The sections below define
each annotation; this section defines what they attach to and how one effective
value is obtained when a definition is reached indirectly.
Attachment. An annotation attaches to the document node being validated — identified by its instance path — not to the schema definition that carries it. The same definition applied at several nodes annotates each node separately, and a definition applied to no node annotates nothing. Consequences:
- A definition used as an array
itemtypeannotates every present item node, not the array. A deprecateditemtypeapplied to an array of one hundred items therefore produces one hundred warnings, one per item node, and never a single warning for the array. - A definition used as a
collectionitemtypeannotates every present dynamically keyed entry, once per entry. - A definition reached through
itemsannotates the one indexed node it validates. - An absent optional slot holds no node, so it carries no annotation and produces no deprecation warning.
Every annotation-derived diagnostic therefore carries the instance path of the node it annotates. The severity and code of such a diagnostic are assigned by Diagnostics; this section defines only what annotations attach to.
Effective annotation. For one document location, the participants that may
carry an annotation are the use site, each definition along that use site's
type reference chain, each allof component of the effective definition, the
oneof or anyof alternative that succeeded, and the conditional branch that
was selected. The general precedence is:
- an annotation declared at the use site takes precedence over the same
annotation obtained through that use site's
typereference chain, and within the chain the nearest declaration takes precedence over a more distant one; allofcomponents, union alternatives, and conditional branches contribute annotations for the location they help validate, but never override an annotation the use site or its reference chain already supplies.
deprecated is an exception to point 2 and does not follow the general
precedence: it combines disjunctively rather than resolving to a single
declaration, so a contributing deprecated = true deprecates the location even
when the use site declares deprecated = false. Deprecation is
authoritative for it. The general precedence governs description and any
future annotation that resolves to at most one value.
Each annotation then resolves its own contributions as follows.
description follows the general precedence and resolves to at most one value.
A use-site description overrides the one carried by the definition it
references, and the nearest description along a reference chain overrides a more
distant one; the use site is the more specific statement about that location, so
it wins. Descriptions carried by allof components, by the successful union
alternative, and by the selected conditional branch never replace that value.
Where a single description is required — an editor hover, for instance — the
effective description is the use-site or reference-chain value if one exists,
and otherwise nothing. An implementation MAY additionally expose the
contributed descriptions as documentation, in a stable order: allof
components in declaration order, then the successful alternative or selected
branch. It MUST NOT merge or concatenate contributions into a value it presents
as a single authored description, and it MUST NOT reject a schema because two
participants carry different descriptions.
default does not follow this general precedence alone, because it is
machine-readable and must resolve to exactly one value. Its precedence and
conflict rules, including the allof and union rules, are defined under
Default and are authoritative.
deprecated is a per-node warning. It fires once for each present document node
whose effective definition is deprecated, and duplicate warnings contributed by
more than one successful path are deduplicated as
Diagnostics requires. It fires per node and never per definition,
so the count follows the attachment rules above. Deprecation is not inherited
downward: a deprecated parent produces one warning at the parent's instance path,
and a descendant produces a warning only when its own effective definition is
deprecated. When a union or conditional definition is itself deprecated, that
warning is reported at the node's own instance path in addition to any warning
contributed by the successful alternative or selected branch, and the two are
distinct instance paths only when the alternative or branch annotates a
descendant.
Description - description
description is an optional human-readable string that documents a schema definition. It may be used on reusable types, elements, and nested definitions. Implementations and tooling MAY use it for documentation, suggestions, and autocompletion; it does not affect validation. Its precedence when a definition is reached through a reference, composition, an alternative, or a conditional branch is defined under Annotations.
[types.game]
type = "table"
description = "A game object."
[types.game.id]
type = "string"
description = "Unique identifier for the game."
[elements.game]
type = "array"
description = "A list of games."
itemtype = "types.game"
Default - default
default is a machine-readable annotation containing any TOML value. Like the
other annotations, it attaches to the document node a definition validates, and
to the slot that node occupies, as described under Annotations;
unlike them, it MUST resolve to exactly one value, and the rules below are
authoritative for that resolution.
Because default is also a legal child key, a default = <value> key/value
entry is always the annotation, while a table header such as
[elements.options.default] is always a child definition named default.
Consequently, a table-valued default MUST use inline-table syntax, for example
default = { min = 1, max = 10 }. A definition that needs both the annotation
and a child named default writes the child through the children namespace,
as described under Quoted and Special Keys.
A default is not a validation assertion and never changes the document being validated. It does not insert a missing value, satisfy a required definition, or change the parsed TOML data returned by an implementation. Tools MAY expose it as a suggestion or effective-configuration hint through schema metadata. Version 1.0 does not define an operation that materializes defaults.
Despite being an annotation, a declared default MUST validate as a present value against the full effective definition at schema-load time. Default validation applies all references, composition, alternatives, fixed children, sibling rules, and ordinary constraints, but does not emit a deprecation warning. A loader MUST reject an incompatible default. This is one of the schema-load checks that read the effective rather than the local view, as Local and Effective Checks records.
A default declared directly at a use site takes precedence over one inherited
through its type reference. Without a use-site default, the referenced
default is inherited. Defaults contributed by allof components are compared using
Parsed Value Equality. Equal defaults are
deduplicated. If components contribute unequal defaults and the local
definition has no default, the schema is malformed because it has no single
effective default. A valid local default resolves that annotation conflict and
must still satisfy every component.
Alternative selectors do not contribute to a slot's effective default. The
effective default of a slot whose selector resolves to oneof or anyof is
determined only by a use-site default, a default inherited through the named
type reference chain that resolves to the union, and defaults composed
through allof, using the precedence and conflict rules above. A default
declared on an individual alternative is never aggregated into the union's
effective default and never causes a schema-load conflict, even when several
anyof alternatives declare unequal defaults. A loader MUST NOT reject a
schema solely because two alternatives declare defaults, equal or unequal.
Resolving effective defaults therefore never requires deciding whether two
alternatives can match the same value.
When a union slot is absent, only this effective default applies; if none
exists, the slot has no default. When the slot value is present, default does
not change it, so alternative defaults surfaced under the successful-alternative
rule are informational hints. An implementation MAY expose the default of each
successful alternative, deduplicated by
Parsed Value Equality, but MUST NOT treat multiple
present-value alternative defaults as an error.
Example:
[elements.retries]
type = "integer"
optional = true
default = 3
Deprecation - deprecated
deprecated is a boolean annotation. When true, it advises that the present
value at that schema location should no longer be used and may be removed in a
future version.
Deprecation never makes a document invalid. A successfully validated present
value produces a warning diagnostic with the code deprecated; an absent
optional value produces no warning. A deprecated parent produces one warning at
the parent's instance path rather than one warning for every descendant. The
instance path a deprecation warning is reported at, and how many warnings a
deprecated array itemtype or collection itemtype produces, follow the
attachment rules under Annotations. Whether a node counts as
successfully validated for this purpose is decided by the annotation step of
Keyword Evaluation Order.
Deprecation propagates through named references. Across allof, any
contributing deprecated = true deprecates the location, and a local
deprecated = false cannot cancel it. Which warning a definition contributes
when it is reached through an alternative or a conditional branch is defined
under Annotations. A non-boolean deprecated
value is a schema-load error. Authors SHOULD use description for migration or
replacement guidance.
Example:
[elements.legacy-timeout]
type = "integer"
deprecated = true
description = "Use request-timeout instead."
optional = true
Constraints
Optionality - optional
A definition may be marked optional in the schema. By default, optional is
false, and the structure is required.
Validators MUST skip a definition only when it is optional and the corresponding value does not exist in the TOML document. In every other case, the validator MUST validate the value against the definition.
This section is the single authority for how optional interacts with
references, composition, alternatives, and conditional branches.
For a named type reference, optionality is inherited: the referencing slot is
optional if either the use site or any definition in the reference chain
declares optional = true. An explicit optional = false cannot cancel an
inherited true. In contrast, optional on the local definition determines
whether a composed node may be absent, and optional values contributed only
through allof components do not affect presence; such a value has meaning only
when that component is referenced normally with type. The presence of a
oneof or anyof slot is governed only by optional on the union definition
or inherited through a named type reference to that union. optional
declared inside an alternative does not make the union slot optional because
no alternative is selected when the slot is absent. Optionality likewise belongs
to a conditional definition rather than to its branches: an optional
annotation inside a then or else branch does not make the conditional slot
optional, because no branch is selected when the slot is absent.
Minimum Value / Maximum Value - min and max
These properties define inclusive value ranges. The comparable kinds are:
floatinteger- date and/or time types:
offset-date-time,local-date-time,local-date, andlocal-time
min and max MAY be declared only on a definition whose type resolves to
exactly one comparable kind, and on an array or a collection whose members
do, as the per-member reading below describes.
A min or max boundary MUST be a TOML value that is comparable with the effective type: integer or float boundaries for integer and float values, and matching temporal boundaries for temporal values.
nan, +nan, and -nan are not valid min or max boundaries because NaN is unordered. inf, +inf, and -inf are valid float boundaries, but they are not valid boundaries on a definition whose comparable kind is integer; an infinite boundary has no integer counterpart and constrains no integer value, so schema loaders MUST reject it there at schema-load time.
Date/time boundaries compare only against values of the same TOML temporal type. For example, an offset-date-time boundary applies to offset-date-time values, not to local-date-time values.
Numeric ranges use mathematical ordering after TOML parsing. Integer-to-integer ordering MUST remain exact across the full signed 64-bit range. Mixed integer/float ordering MUST compare the exact integer with the parsed binary floating-point value without first rounding the integer to that floating-point format. Positive and negative zero have the same position. A document NaN is unordered and never satisfies a range constraint.
Offset date-times are ordered by the instant they identify after applying their offset; representations of the same instant compare equal even when their local fields and offsets differ. Local date-times, local dates, and local times are ordered lexicographically by their parsed fields, from the largest component to the smallest, including fractional seconds. No timezone or daylight-saving conversion is applied to a local temporal value.
When both min and max are present, min MUST be less than or equal to max
under the same ordering this section defines for validation: mathematical
ordering for numeric boundaries, instant ordering for offset-date-time
boundaries, and parsed-field ordering for local-date-time, local-date, and
local-time boundaries. A schema violating that rule is malformed and schema
loaders MUST reject it at schema-load time. The comparison is always defined
because the unordered boundary values excluded above are not valid boundaries in
the first place.
On an array or a collection, min and max are
per-member constraints and apply to each item
or each dynamic entry. That section is authoritative for the per-member reading,
for how the member type is resolved through itemtype and through the
alternatives of a referenced oneof or anyof, and for the schema-load
rejection of a member type that is indeterminate or of the wrong kind. The
interaction between array range
boundaries and allowedvalues is defined under
Observations on Conditions to Arrays.
Length - minlength and maxlength
These properties MUST be used only to define the allowed length of a string,
an array, or a collection.
minlength and maxlength are valid only on definitions whose selected type is
the built-in string, array, or collection. They cannot be attached to
another built-in type, an alternative selector, or a named type reference, as
Schema Definition Properties requires.
Schema loaders MUST reject an incompatible length constraint at schema-load time rather
than silently ignoring it.
Both minlength and maxlength MUST be integers >= 0. When both are present, minlength MUST be less than or equal to maxlength. A schema violating either rule is malformed and schema loaders MUST reject it at schema-load time.
For string values, length is counted as the number of Unicode scalar values after TOML parsing and escape processing. It is not the number of UTF-8 bytes, UTF-16 code units, or user-perceived grapheme clusters. For example, "\U0001F600" has length 1, while "e\u0301" has length 2 because it is composed of two Unicode scalar values.
For an array, length is its number of items. For a collection, length is
the number of dynamic entries to which itemtype applies; fixed child
definitions are excluded from the count.
On an array or a collection these two properties always bound the
container's own member count. They are deliberately excluded from the
per-member value-constraint subset, which
explains why, and a per-member length bound is written on the definition the
container's itemtype reaches.
Pattern - pattern
pattern constrains a parsed TOML string with a regular expression.
pattern is valid on a definition whose selected type is the built-in
string, and, as a per-member constraint, on a
non-tuple array or collection whose effective member type is the built-in
string. It cannot be attached to another built-in type, an alternative
selector, or a named type reference, as
Schema Definition Properties requires. Schema
loaders MUST reject an incompatible
pattern at schema-load time rather than silently ignoring it. On a
collection, pattern constrains dynamic entry values while
keypattern constrains their keys.
The portable TOML Schema regular-expression profile consists of literals,
escaped metacharacters, the character escapes \t, \n, \r, \f, \v, and
\a, ., character classes and ranges, negated character
classes, concatenation, alternation, capturing and non-capturing groups, the
anchors ^ and $, and the greedy quantifiers ?, *, +, {n}, {n,},
and {n,m}. These constructs use the syntax documented by the
RE2 syntax reference. Implementations MUST
support this profile.
The listed character escapes are portable because each denotes one fixed control
character, so no engine can disagree about what it matches. They are portable
both standalone and inside a character class, so [ \t] is a portable pattern.
Character-class shorthands such as \d, \s, and \w are outside the
portable profile because regular-expression engines disagree about whether
they use ASCII or Unicode membership. Write the intended set explicitly instead:
[0-9] rather than \d. Unicode property classes such as
\p{L}, inline flag groups such as (?i), backreferences, look-around
assertions, atomic groups, conditionals, recursion, and the non-greedy and
possessive quantifier forms such as *? and *+ are also outside the
portable profile.
Patterns are compiled at schema-load time. A schema loader MUST compile
every pattern value when it loads the schema, whether or not any document
exercises it. A pattern that does not compile — "[", "{2,1}", or a trailing
backslash, for example — makes the schema malformed, and the loader MUST reject
it at schema-load time with the invalid-pattern code. An implementation MUST
NOT defer a compilation failure to match time, MUST NOT treat an uncompilable
pattern as a failed match, and MUST NOT treat it as a satisfied constraint.
A construct outside the portable profile is likewise a schema-load error,
reported with the unsupported-pattern code. A loader operating in its
conformant TOML Schema 1.0 mode MUST reject a pattern
that uses one, even when the underlying engine could compile it. An
implementation MAY offer an additional, explicitly named and documented extended
pattern profile that accepts further constructs, but that profile MUST NOT be
the default, and a schema accepted only under it is not a TOML Schema 1.0
schema. Portability is the whole purpose of naming a profile: if a
non-portable construct were merely discouraged, the same schema would be a
load error on one implementation, an ASCII-only match on a second, and a
Unicode match on a third, which is precisely the interoperability split the
profile exists to prevent. Conformance suites MUST use only the portable
profile. The engine an implementation matches these expressions with, and the
resource limits it applies to compilation and matching, are additionally
constrained by
Regular-Expression Safety.
Unicode semantics. A pattern is compiled from, and matched against, the
decoded parsed string: a sequence of Unicode scalar values. The TOML parser has
already applied string escapes such as \u00E9, so a loader never sees TOML
escape syntax in a pattern. Matching operates on Unicode scalar values, never on
UTF-8 bytes, UTF-16 code units, or grapheme clusters, and the count semantics of
{n,m} follow that same unit. . matches exactly one scalar value other than
a line feed. A character class matches one scalar value from its members, and a
range such as [a-z] or [À-Ö] is an inclusive range over Unicode code
points; a range whose start code point is greater than its end code point does
not compile and is therefore a schema-load error. A negated class matches any
scalar value not listed, including a line feed.
Matching is case-sensitive and applies no case folding. No Unicode normalization is applied to the pattern or to the subject before matching: both are compared as the exact decoded scalar sequences the TOML parser produced, consistent with the string rule under Parsed Value Equality. Two strings that are canonically equivalent but differently composed are therefore distinct subjects, and an implementation MUST NOT normalize either operand.
The pattern is not implicitly anchored. A value validates if the regular
expression matches anywhere in the string. Authors who require a full-string
match MUST anchor the expression with ^ and $.
Patterns are evaluated without multiline or dot-all modes. ^ matches only
the start of the complete parsed string, $ matches only its end (not a
position before a final line feed), and . does not match a line feed. These
rules also apply when an implementation uses a regular-expression engine with
different defaults.
String Format - format
format applies a standardized semantic assertion to a parsed TOML string.
format is valid on a definition whose selected type is the built-in
string, and, as a per-member constraint, on a
non-tuple array or collection whose effective member type is the built-in
string. It cannot be attached to another built-in type, an alternative
selector, or a named type reference, as
Schema Definition Properties requires. A schema
loader MUST reject an unsupported
format name or incompatible use at schema-load time rather than ignoring it.
The value of format MUST be one of the following case-sensitive names:
| Format | Required syntax |
|---|---|
email |
An ASCII SMTP Mailbox as defined by RFC 5321, section 4.1.2, with the length limits from section 4.5.3.1 |
uuid |
The hexadecimal-and-dash UUID representation defined by RFC 9562, section 4 |
uri |
An absolute URI as defined by RFC 3986, including a scheme |
hostname |
The preferred ASCII host-name syntax from RFC 1123, section 2.1 |
ipv4 |
IPv4 dotted-decimal notation |
ipv6 |
IPv6 text representation as defined by RFC 4291, section 2.2 |
The formats use the following portable rules:
emailaccepts the RFC 5321 dot-string and quoted-string local-part forms. It is not an internationalized mailbox format: every character MUST be ASCII. The local-part MUST be at most 64 octets and the complete mailbox MUST be at most 254 octets. The domain MUST be an RFC 1123 host name or an RFC 5321address-literalas defined by RFC 5321, including IPv4, IPv6, and registered General-address-literal forms. Validators MUST parse these structures and MUST NOT substitute a simplifiedtext@textregular expression.uuidconsists of exactly 32 hexadecimal digits, case-insensitive, displayed in groups of 8, 4, 4, 4, and 12 digits separated by hyphens.uriMUST match the RFC 3986URIproduction rather thanrelative-ref. It is ASCII; non-ASCII components MUST be percent-encoded. Every percent escape MUST contain exactly two hexadecimal digits.hostnameis ASCII and case-insensitive. After excluding one optional final root dot, its total length MUST be from 1 through 253 characters. Each dot-separated label MUST contain 1 through 63 ASCII letters, digits, or hyphens, MUST begin and end with a letter or digit, and MAY be entirely numeric.ipv4contains exactly four decimal octets from 0 through 255 separated by dots. An octet MUST NOT contain a leading zero unless the octet is exactly0.ipv6accepts the compressed and IPv4-embedded forms defined by RFC 4291. It does not accept a URI host's brackets or a zone identifier. An embedded IPv4 suffix follows theipv4rules above.
When format is combined with allowedvalues, every allowed string MUST
satisfy the format at schema-load time. format is independent of pattern,
minlength, and maxlength; a document string MUST satisfy every declared
constraint.
[elements.contact]
type = "string"
format = "email"
[elements.endpoint]
type = "string"
format = "uri"
[elements.instance-id]
type = "string"
format = "uuid"
Allowed Values - allowedvalues
allowedvalues provides an enumeration for a scalar or unconstrained built-in
type. On an array or a collection it is instead a
per-member constraint and enumerates the values
permitted for each item or each dynamic entry, as also described
under Observations on Conditions to Arrays.
It is invalid on a table.
The allowedvalues array MUST contain at least one entry. Every entry on a
definition that is neither an array nor a collection MUST have the TOML kind
selected by that definition;
type = "any" is the exception and permits entries of any TOML kind. Numeric
equality between integers and floats does not make their TOML kinds
interchangeable for this schema-load check. A malformed enumeration MUST be
rejected at schema-load time.
For every definition, when allowedvalues is combined on the same definition
with a constraint that applies to the same values the enumeration describes,
every entry in allowedvalues MUST satisfy that constraint, and schema loaders
MUST reject a violating entry at schema-load time. The constraints in scope are
pattern, format, min, and max for every definition, plus minlength and
maxlength for a definition that is neither an array nor a collection. On an
array or a collection, minlength and maxlength bound the container itself
rather than its members, so they are not part of this per-entry check. This is a
consistency check on the enumeration itself; it describes nothing about how a
document value is validated. For offset date-times, this boundary check uses
instant ordering even though subsequent allowedvalues membership uses parsed-value
equality; equivalent instants with different retained local fields or offsets
therefore compare equal for a boundary but remain distinct enumeration values.
After a schema with allowedvalues has been loaded successfully, each document
value governed by that definition — each item or dynamic-entry value, for an
array or collection definition —
is evaluated in the following order:
- The value's parsed TOML kind MUST be the kind the definition selects for it,
through
typeor, for container members, throughitemtype. A kind mismatch is a validation error regardless ofallowedvalues, and membership in the enumeration never satisfies the type check. For example, a definition withtype = "integer"andallowedvalues = [ 80, 443 ]rejects the document value80.0even though Parsed Value Equality makes80.0equal to80. The unconstrainedanyselects no kind and imposes no such restriction. - Every other assertion that applies to the value is evaluated, including the
assertions declared on the definition itself and those contributed by
allofcomponents. Validators MUST NOT skip an assertion on the grounds that the enumeration was already checked while loading the schema: that schema-load check covers only the definition's own constraints listed above, and a composed constraint does not participate in it. - The value MUST be a member of
allowedvaluesaccording to Parsed Value Equality.
The value is valid for that definition only when all three steps succeed.
Example:
[types.colorType]
type="string"
allowedvalues=[ "red", "black", "blue" ]
Array Uniqueness - uniqueitems
uniqueitems is a boolean property valid only on a definition whose selected
type is the built-in array. When it is true, no two items in the document
array may be equal. When it is false or absent, the schema imposes no
uniqueness condition. It applies to homogeneous arrays, tuple arrays, and
arrays whose item type is otherwise unconstrained.
Items are compared using Parsed Value Equality, recursively for arrays and tables.
Uniqueness compares complete item values. Version 1.0 does not define a
field-selecting operation such as uniqueBy; two tables that share an id but
differ elsewhere remain distinct. A schema loader MUST reject a non-boolean
uniqueitems value or its use on a non-array definition.
Per-Member Value Constraints
Five value constraints form the per-member value-constraint subset:
allowedvalues, min, max, pattern, and format. Any of them MAY be
declared directly on a definition that selects the built-in type = "array" or
the built-in type = "collection". Declared there, a constraint does not
describe the container. It applies to each item of the array and to each
dynamically keyed entry of the collection. The subset is identical for the two
containers, because an array item and a collection entry are the same thing: one
member value that the container's itemtype governs.
Declaring a per-member constraint inline is exactly equivalent to declaring that
same constraint on the member definition the container's itemtype reaches. The
two spellings MUST validate identically, and authors MAY choose either. Inline
is the shorter spelling for a container whose members need one or two
constraints; a named itemtype definition is the reusable one.
[elements.ports]
type = "array"
itemtype = "integer"
min = 1
max = 65535
[elements.hosts]
type = "collection"
itemtype = "string"
format = "hostname"
keypattern = "^[a-z][a-z0-9-]*$"
On a collection, pattern constrains each dynamic entry's value while
keypattern constrains each dynamic entry's key. The two are independent,
apply to different subjects, and MAY be combined on the same definition.
Because the two spellings are equivalent, declaring the same constraint both
inline and on the resolved itemtype definition is a schema-load error, and
schema loaders MUST reject it with exclusive-properties. This specification
defines no precedence between them and needs none: a schema that would say the
same thing twice says it once instead. Different constraints MAY be split across
the two spellings and then apply conjunctively. The check compares the inline
constraint against the constraint the resolved itemtype definition declares
itself; a constraint that definition acquires from its own allof is not part
of the check and merges conjunctively as
Merging by TOML Kind requires.
A per-member constraint obeys the same applicability rule its own section states, applied to the member type rather than to the container:
patternandformatrequire the effective member type to be the built-instring, as Pattern and String Format require of a string constraint.minandmaxrequire the effective member type to resolve to exactly one comparable kind, as Minimum Value / Maximum Value requires.- every
allowedvaluesentry MUST have a TOML kind the effective member type permits, as Allowed Values requires.
The member type is the one the container's itemtype selects, resolved through
named references and through the alternatives of a referenced oneof or
anyof, which MUST all resolve to the same kind. When a container declares no
itemtype, its members are unconstrained any: allowedvalues still applies
and enumerates values of any TOML kind, while min, max, pattern, and
format have no determinate member kind to apply to. Schema loaders MUST reject
a per-member constraint whose member type is indeterminate or of the wrong kind
at schema-load time.
items is mutually exclusive with the whole subset. A tuple position is
described by the reusable definition named at that position, so a constraint
that would apply uniformly to every member has no meaning beside a positional
definition.
minlength and maxlength are deliberately not in the subset. On an
array or a collection those two spellings are already taken: they constrain
the container, bounding an array's item count and a collection's
dynamic-entry count, as Length defines.
That meaning is unchanged. One spelling cannot mean both "this container holds
at least two members" and "each member is a string of at least two characters",
and reinterpreting it per member on a container whose members happen to be
strings would make the same keyword mean different things in two schemas that
differ only in their itemtype. Per-member string length is therefore expressed
through itemtype:
[types.shortName]
type = "string"
minlength = 1
maxlength = 32
[elements.tags]
type = "array"
itemtype = "types.shortName"
minlength = 1
Here minlength = 1 on elements.tags requires at least one tag, while the
bounds inside types.shortName govern each tag's length in characters. This is
the one exception to the symmetry above, and it exists because the spelling is
occupied, not because container members are otherwise privileged.
Sibling Presence Rules
Version 1.0 defines three presence-only rules for direct fixed children of an
effective table or collection. They inspect parsed key presence, never a
child's value, and never follow a dotted string as a document path.
Dependencies - dependentrequired
dependentrequired is a non-empty inline table. Each member maps one trigger
child name to a non-empty array of unique child names. When the trigger is
present, every listed child MUST also be present.
Because dependentrequired is also a legal child key, a
dependentrequired = { ... } key/value entry is always the sibling rule, while
a table header such as [types.example.dependentrequired] is always a child
definition named dependentrequired. A definition that needs both the rule and
a child of that name writes the child through the children namespace, as
described under Quoted and Special Keys.
Dependencies are directional. If a requires b, the presence of b does not
require a unless a reverse mapping is declared. Every triggered mapping is
evaluated, so dependencies may apply transitively.
Example:
[types.dependency]
type = "table"
dependentrequired = { branch = [ "git" ], tag = [ "git" ], rev = [ "git" ] }
[types.dependency.git]
type = "string"
optional = true
[types.dependency.branch]
type = "string"
optional = true
[types.dependency.tag]
type = "string"
optional = true
[types.dependency.rev]
type = "string"
optional = true
Mutual Exclusion - mutuallyexclusive
mutuallyexclusive is a non-empty array of groups. Each group is an array of
at least two unique child-name strings. At most one member of each group MAY be
present.
Zero or one present member satisfies a group.
Example:
[types.source]
type = "table"
mutuallyexclusive = [ [ "git", "path" ], [ "branch", "tag", "rev" ] ]
[types.source.git]
type = "string"
optional = true
[types.source.path]
type = "string"
optional = true
[types.source.branch]
type = "string"
optional = true
[types.source.tag]
type = "string"
optional = true
[types.source.rev]
type = "string"
optional = true
Exactly One - exactlyone
exactlyone has the same shape as mutuallyexclusive, but exactly one member
of every group MUST be present.
This allows every group member to remain individually optional = true while
the group still requires one choice.
Example:
[types.readmeTable]
type = "table"
exactlyone = [ [ "file", "text" ] ]
[types.readmeTable.file]
type = "string"
optional = true
[types.readmeTable.text]
type = "string"
optional = true
Every name in these three properties MUST identify a direct fixed child in the
determinate fixed-child set of the definition
after allof composition. Operand resolution and the table-or-collection
applicability of these rules are therefore effective rather than local checks,
as Local and Effective Checks records. Operands
are resolved when the schema is loaded, so
they MUST NOT be resolved against the validation-time
effective closure set; only the presence check itself
happens while a document is validated. A quoted string containing a
dot identifies a literal dotted child key, and a child written through the
children namespace is named by its target key rather than by the literal
string children. Dynamic collection keys are not fixed children and cannot be
operands, while a collection's explicitly defined children participate normally.
Because a oneof or anyof selector and a conditional selector contribute
nothing to the determinate set, an operand that only such an alternative or
branch could supply names no fixed child and MUST be rejected at schema-load
time. Declare those operands as local fixed children instead — optional = true
ones when the document may omit them — or move the rule into the alternatives or
branches that define the children.
Schema loaders MUST reject a rule with the wrong TOML value type, an empty mapping or group list, a group with fewer than two members, a duplicate name within one dependency array or group, an unknown fixed-child name, or use on an incompatible effective type. Loaders are not required to prove general logical satisfiability between multiple valid rules.
Ordinary requiredness is evaluated together with these rules. An absent optional trigger has no effect. A non-optional child remains required even if a presence group would otherwise permit its absence.
Container Types
- Array:
array - Table:
table - Collection:
collection
array and table correspond to parsed TOML value kinds. collection is a
schema-level specialization of table for dynamically named entries. TOML
inline tables parse as table values and therefore do not need a separate
built-in type.
Tables
A definition whose effective type is table matches a parsed TOML table value.
Table headers and inline tables produce the same value kind and are validated
identically.
If a schema definition has nested child definitions but does not declare a selector, it is an implicit table, as Selectors defines.
The fixed children of a table, for the purpose of the rules below, are its
own nested child definitions together with the children contributed by every
allof component and by the union alternative or conditional branch selected
for the document value being validated. That set is the table's
effective closure set. Sibling presence rules resolve
their operands against a narrower, schema-load-time set instead; see
Determinate Fixed-Child Set.
A table with at least one fixed child is closed. Validators MUST apply all of the following rules to it:
- every fixed child that is not optional MUST be present in the document table;
- every present fixed child's value MUST validate against its definition; and
- every document key that is not a fixed child MUST be reported as an unknown-key error.
The third rule is what makes a closed table reject misspelled and undeclared keys. It is the same rule the root applies under Elements Table.
A table with no fixed children is open. Validators MUST accept any TOML
table value without validating its contents, including its keys. This is useful
for representing custom data payloads. Openness is a property of the effective
closure set, so a table is open only when neither the local definition, nor any
allof component, nor the selected alternative or branch contributes a fixed
child. Openness and unknown-key rejection are two of the checks that read the
effective rather than the local view; the complete list is under
Local and Effective Checks.
An open table is not the same as an empty root [elements] table. An open table
accepts any keys; an empty [elements] table accepts no application data at
all, as described under Elements Table.
Arrays
Arrays can be defined using the following properties:
itemtype: a type reference used to validate every item in a homogeneous array.items: ordered type references for tuple-style positional validation with fixed arity.minlength: the minimum length of the array (e.g. no less than 2 elements).maxlength: the maximum length of the array (e.g. no more than 2 elements).min: the minimum value allowed for each comparable array item (e.g. 80).max: the maximum value allowed for each comparable array item (e.g. 8080).allowedvalues: enumeration of the values permitted for each item.pattern: a regular expression each string item must match.format: a standardized semantic format each string item must satisfy.uniqueitems: whether every parsed array item must be unique.
min, max, allowedvalues, pattern, and format are the
per-member value-constraint subset; they
constrain each item rather than the array, and a collection accepts the same
five with the same meaning. minlength and maxlength bound the array itself.
Example for schema definition:
[elements.colors]
type="array"
itemtype="string"
Example of TOML file:
colors=[ "red", "yellow", "green" ]
Observations on Conditions to Arrays
The min and max conditions set an inclusive range for every array item.
Per-Member Value Constraints is authoritative
for that per-item reading, for the shared subset min, max, allowedvalues,
pattern, and format, for how a named itemtype and its alternatives are
resolved, and for the equivalence of an inline constraint to the same constraint
written on the itemtype definition. The comparable kinds and the ordering
rules used for numeric and temporal items are defined under
Minimum Value / Maximum Value.
When allowedvalues is present on an array, every array item MUST be a member
of that enumeration. The enumeration does not have to be sorted.
Allowed Values is authoritative for the
schema-load consistency check between allowedvalues and a sibling per-member
constraint such as min or max, including the requirement that a loader
reject an enumerated value violating an applicable inclusive boundary.
When the array declares itemtype, every enumerated value MUST have a TOML
kind permitted by the effective item type, as
Per-Member Value Constraints requires. An
itemtype that permits any permits enumeration entries of any TOML kind. This
schema-load check verifies the permitted TOML kind; constraints inside a named
item definition still apply normally when a document array is validated.
minlength and maxlength constrain the document array's item count, not the
number of entries in allowedvalues, and never the length of an individual
item; see Per-Member Value Constraints.
If neither itemtype nor items is defined, array items default to any, so
items of different TOML types may be mixed.
If type = "array" and itemtype = "array", every item MUST be an array. The
contents of those nested arrays are unconstrained; only omitting itemtype
permits non-array and array items to be mixed in the outer array.
Array Item Schemas and Arrays of Tables
itemtype accepts the references
Type Reference Restrictions permits at that
position. Use a
built-in reference such as itemtype = "string" for a homogeneous scalar
array, or a reusable schema definition when members require constraints or
structure. A reusable table definition is required for TOML arrays of tables
and arrays of inline tables because both parse as arrays whose items are table
values. The same keyword selects the type of each dynamic value in a
collection.
Example with TOML arrays of tables:
[[products]]
name = "Hammer"
sku = 738594937
[[products]]
name = "Nail"
sku = 284758393
Schema:
[types.productType]
type = "table"
[types.productType.name]
type = "string"
[types.productType.sku]
type = "integer"
[elements.products]
type = "array"
itemtype = "types.productType"
Example with TOML arrays of inline tables:
points = [
{ x = 1, y = 2 },
{ x = 3, y = 4 }
]
Schema:
[types.pointType]
type = "table"
[types.pointType.x]
type = "integer"
[types.pointType.y]
type = "integer"
[elements.points]
type = "array"
itemtype = "types.pointType"
Tuple / Positional Array Validation - items
Use items to validate each array entry by position with an exact length.
Semantics:
itemsis ordered, and each index validates against the corresponding referenced type.itemsMUST contain at least one type reference. A schema loader MUST rejectitems = []; to require an empty array, omititemsand declaremaxlength = 0instead.- When
itemsis present, the array MUST have exactly the same number of items. itemsis mutually exclusive withitemtype.itemsis also mutually exclusive withminlengthandmaxlength.itemsis mutually exclusive with the whole per-member value-constraint subset:allowedvalues,min,max,pattern, andformat. Constraints for a tuple position belong in the reusable definition referenced at that position. The exclusion is local, as Local and Effective Checks states, so anallofcomponent MAY still contribute one of those constraints conjunctively.itemsMAY name the same type reference more than once. Each entry denotes a position rather than an alternative, so a tuple whose positions share a type, such asitems = [ "types.coordinate", "types.coordinate" ], is valid and repetition is meaningful.
Example:
[types.coordinate]
type = "float"
[types.label]
type = "string"
[types.coordinateLabel]
type = "array"
items = [ "types.coordinate", "types.label" ]
Collection of Elements for Dynamic Keys
One can set an element of type collection when there is a need to have multiple children with dynamic, user-provided keys or table headers.
A collection is represented by a TOML table and may have fixed child
definitions in addition to dynamically named entries. It remains a distinct
schema type from table because it applies itemtype and collection
unknown-key semantics to dynamic entries. Fixed children may use any schema
definition, including nested tables, arrays, and named types.
A collection requires at least one effective itemtype constraint to define
the type of its dynamic child values. The constraint may be declared locally or
contributed by a compatible allof component; this is the collection instance
of the general principle stated under
Composition Supplying the Local Skeleton,
not a rule peculiar to collections. Each dynamic child must be given
a unique key in the TOML document. itemtype accepts the references
Type Reference Restrictions permits at that
position. A schema loader MUST reject an effective collection
when neither its local definition nor any referenced or composed definition
contributes an itemtype. This is a schema-load check, so it reads only the
contributions that are determinate at schema-load time; which components make
one is defined under
Determinate Fixed-Child Set. It is one of the
checks that read the effective rather than the local view, as
Local and Effective Checks enumerates.
The built-in collection cannot itself be used as itemtype or as an entry in
items, oneof, anyof, or allof, as
Type Reference Restrictions states: those bare
references provide no place to declare
the nested collection's required itemtype. Define a reusable collection with
its own itemtype and reference that named definition instead.
When collection values may have alternative types, define those alternatives in a reusable [types] definition with oneof or anyof, then reference that definition with itemtype. This keeps oneof and anyof consistently scoped to the current node rather than changing their meaning on a container.
Unlike a table, a collection is never closed. Validators MUST classify every
key of the document table as either a fixed child or a dynamic entry, using the
collection's effective closure set to decide which. A
key that names a fixed child definition is validated by that definition. Every
other key is a dynamic entry: its name MUST satisfy every applicable
keypattern and its
value MUST validate against every applicable itemtype. A collection therefore
never produces an unknown-key error; an undeclared key that is not acceptable is
reported as a keypattern failure on the key, or as an ordinary failure of the
entry's value against the item definition, as
Code Registry requires. Fixed children that
are not optional remain required, exactly as in a closed table.
This difference in unknown-key semantics is why table and collection are not
interchangeable for allof composition. Because a collection is never closed,
schema authors SHOULD prefer a closed table when the key set really is fixed.
A collection may additionally constrain the keys (entry names) of its dynamic children with keypattern. See Key Pattern - keypattern.
A collection may also constrain the values of its dynamic entries inline,
with allowedvalues, min, max, pattern, or format, instead of pushing
each such constraint into a named itemtype definition. Those five properties
behave identically on an array and on a collection; see
Per-Member Value Constraints.
This precedence is what lets a collection validate known keys precisely while
applying itemtype only to all other keys. For example, itemtype = "any"
makes unknown keys forward-compatible while fixed children still receive their
declared validation. Authors choosing this pattern trade typo detection on
unknown keys for extensibility; use a closed table when undeclared keys must
be rejected. Schema authors SHOULD prefer a closed table when the upstream
format defines a stable, exhaustive key set.
This precedence also supports open extension namespaces with typed well-known
entries. For example, a pyproject.toml schema can define [tool] as a
collection of open tables, then add fixed [tool.ruff] and [tool.uv] child
definitions with their respective schemas. Other tool names continue to use the
collection's general itemtype.
Example:
The example below shows a table servers that is a collection.
Each server MUST be given a key and follow the defined structure of types.serverType.
A server may also have a DNS table with user-provided key names.
TOML:
[servers]
group = "group1"
[servers.alpha]
name = "Alpha DC0"
address = "dc0.alpha"
[servers.alpha.dnstable]
cloudflare = "1.1.1.1"
google1 = "4.4.4.4"
google2 = "8.8.8.8"
internal = "mydns.intranet"
[servers.beta]
name = "Beta DC0"
address = "dc0.beta"
TOML Schema:
[types]
[types.dnsType]
type = "string"
pattern = "<ip-regex-pattern>"
[types.hostnameType]
type = "string"
pattern = "<valid-hostname-regex-pattern>"
[types.dnsValue]
anyof = [ "types.dnsType", "types.hostnameType" ]
[types.serverType]
type = "table"
[types.serverType.name]
type = "string"
[types.serverType.address]
type = "string"
[types.serverType.dnstable]
type = "collection"
itemtype = "types.dnsValue"
optional = true
[elements]
[elements.servers]
type = "collection"
itemtype = "types.serverType"
[elements.servers.group]
type = "string"
A collection may be represented as subtables of a common table in a TOML document.
Key Pattern - keypattern
keypattern constrains the keys of a collection's dynamically named entries
with a regular expression.
keypattern may only be used on a collection. It constrains the keys (entry names) of the
collection's dynamic children: every dynamically keyed entry must match the provided regular
expression. It does not validate entry values — that is the role of itemtype. It is
therefore orthogonal to itemtype and may be combined with it.
keypattern is invalid on any non-collection type (scalars, array, plain
table), and a schema loader MUST reject a schema that uses it elsewhere.
Keys that are explicitly declared as fixed child definitions of the collection (schema-restricted
key-value pairs) are validated by their own definitions and are not subject to keypattern. Only
dynamic, user-provided keys are matched against the pattern.
Implementations MUST support the same portable RE2 regular-expression profile as
pattern, and every rule that section states about
regular expressions applies unchanged to keypattern: schema-load compilation,
rejection of uncompilable patterns and of constructs outside the portable
profile, Unicode scalar-value matching, character-class and range behavior,
case sensitivity, and the absence of Unicode normalization. The subject is the
decoded TOML key rather than a string value; keys are matched as the exact
decoded scalar sequences the TOML parser produced, so a quoted key and a bare
key that decode to the same characters are the same subject. Like pattern,
keypattern is not implicitly
anchored: a key validates if the regular expression matches anywhere in the key
string. Authors who require a full-key match MUST anchor the expression with
^ and $.
Example:
[types.listOfServersType]
type = "collection"
itemtype = "types.serverType"
minlength = 1
keypattern = "^server_[0-9]+$"
Against a TOML document:
[servers.server_01] # accepted
[servers.server_02] # accepted
[servers.alpha] # rejected: key does not match ^server_[0-9]+$
This mirrors JSON Schema's propertyNames: { pattern: ... }, applied to TOML maps.
Conjunctive Composition - allof
allof applies a non-empty array of type references to the current node in
addition to its local definition. It is an applicator, not a type selector: it
never chooses between alternatives and never defers a decision to a document
value. The local definition normally declares its own type, oneof, anyof,
or conditional selector, or has fixed children that make it an implicit table;
Composition Supplying the Local Skeleton
defines the one case in which the composition supplies that skeleton instead.
[types.packageBase]
type = "table"
[types.packageBase.version]
type = "string"
[types.package]
type = "table"
allof = [ "types.packageBase" ]
[types.package.name]
type = "string"
Composition Supplying the Local Skeleton
One principle governs how much of a definition allof may supply:
composition MAY supply the local skeleton when it determines that skeleton
unambiguously. A definition need not restate a structural fact its components
already fix beyond doubt, and it MUST state anything they leave open.
A definition whose only applicator is a non-empty allof — one that declares no
type, no oneof, no anyof, no part of the conditional triple, and no nested
child definitions — is a pure mixin. A pure mixin is valid when the
effective types of its allof components resolve to exactly one TOML kind. That
kind becomes the definition's effective type, exactly as if type had named it:
[types.packageBase]
type = "table"
[types.packageBase.version]
type = "string"
[types.named]
type = "table"
[types.named.name]
type = "string"
[types.package]
allof = [ "types.packageBase", "types.named" ]
types.package has the effective type table and the fixed children version
and name. Writing type = "table" beside that allof remains valid and adds
nothing.
When the components resolve to more than one TOML kind, or when the kind cannot
be determined at schema-load time, the composition determines no skeleton and
there is nothing for the definition to inherit. Schema loaders MUST reject such
a definition at schema-load time with incompatible-composition. Authors
resolve the ambiguity by declaring the intended type with type, which turns an
indeterminate composition into an ordinary compatibility failure against a
stated kind. Determining a pure mixin's effective type is one of the checks that
read the effective rather than the local view; see
Local and Effective Checks.
A pure mixin is still an application and never a selection. Because allof is
not a selector, its components contribute their fixed children to the
definition's determinate fixed-child set, and
any itemtype and keypattern they declare are likewise determinate, exactly
as they are for a definition that also declares type. Nothing about a pure
mixin is deferred to validation time.
The same principle governs the one other place a composition supplies a
required local element: a collection may obtain its mandatory itemtype from
a compatible allof component, as
Collection of Elements for Dynamic Keys
describes. That allowance is a consequence of this principle rather than a
special case for collections.
A definition that declares no selector, no nested child definition, and no
allof determines nothing at all, and it remains a schema-load error under
Selectors.
The Effective Definition
allof does not validate the document value separately against the local
definition and against each component in isolation. It first composes an
effective definition by merging the local definition with every referenced
component, and the document value is then validated against that single
effective definition. Merging follows two different rules, and the difference
between them is normative:
- Assertions merge conjunctively. Every assertion declared by the local definition or by any component applies to the value, and no assertion overrides another. If two participants constrain the same value, both constraints apply. A contradiction may therefore describe an effective definition for which no document value is valid; it does not create last-wins behavior.
- Structure merges by union. The fixed-child names contributed by the local definition and by every component are unioned into one set before any unknown-key, requiredness, or sibling rule is applied, and openness or closure is decided from that merged set rather than participant by participant. Two such sets are defined below, computed at two different times and read by different rules: see Determinate Fixed-Child Set and Effective Closure Set.
The two rules MUST NOT be conflated, because they give opposite answers for the
primary use of allof. Take a closed table A with the single fixed child
x, a closed table B with the single fixed child y, and a definition that
composes both. The document table { x = 1, y = 2 } is valid: the effective
closure set is { x, y }, so neither key is unknown. Checking that same
table separately against A and against B would instead make y unknown to
A and x unknown to B, rejecting every document and making mixin
composition useless. Only the effective-definition reading is conforming.
Composition compatibility is judged from the effective view, which is one of the checks Local and Effective Checks lists as effective; every other applicability and exclusivity check remains local.
A composition is well formed only when its participants agree on kind.
Every component MUST resolve to an effective type compatible with the
local definition, or, when the local definition is a pure mixin and states no
kind of its own, with one another. When the local selector is oneof or
anyof, all of its
alternatives MUST resolve to the same effective type before allof can be
applied; a multi-kind local union combined with allof is indeterminate and
MUST be rejected at schema-load time. Scalar and array components MUST have the
same kind as the local definition. Structured components MUST all be table or
all be collection; a table and a collection are not interchangeable for
composition because they have different unknown-key semantics. A component
whose alternatives resolve to different kinds is likewise indeterminate and
MUST be rejected. Which references allof accepts at all is stated under
Type Reference Restrictions.
allof MUST NOT list the same component twice. Duplication is judged on
resolved identity, after the optional types. prefix has been removed, exactly
as it is for union alternatives under
Alternative Types - oneof and anyof.
allof = [ "types.a", "a" ] therefore names one component twice and is
malformed; schema loaders MUST reject it at schema-load time. A repeated
component could never change the effective definition, because both merge rules
above are idempotent.
Merging by TOML Kind
The following rules define the effective definition for each TOML kind a
composition may produce. Throughout, participant means the local definition
or any allof component.
For a composed scalar, the effective definition carries every format,
pattern, allowedvalues, min, max, minlength, and maxlength
declared by any participant, and the document value MUST satisfy all of them.
Two allowedvalues enumerations therefore behave as an intersection, and two
boundaries narrow to the greatest min and the least max. The schema-load
consistency check described under
Allowed Values - allowedvalues inspects one
definition at a time and never inspects composed constraints, so a composed
constraint is always evaluated at document-validation time.
For a composed array, the effective definition carries every array assertion
declared by any participant. Homogeneous itemtype constraints from different
participants all apply to every item. A tuple items constraint contributes its
exact arity and its per-position definitions, and it applies alongside, rather
than instead of, any homogeneous or tuple constraint contributed elsewhere: each
item MUST satisfy its positional definition and every contributed itemtype.
Length bounds narrow to the greatest minlength and the least maxlength,
uniqueitems = true from any participant applies to the whole array and cannot
be cancelled by uniqueitems = false elsewhere, and item enumeration and item
range constraints likewise remain conjunctive. Two participants declaring
different arities, or a tuple arity outside a contributed length bound, make the
effective definition unsatisfiable. Such conflicts do not use last-wins merging
and are not by themselves a schema-load error.
For a composed table, validators MUST form the effective closure set before applying unknown-key rules. The value of a fixed child named by more than one participant MUST validate against every contributing child definition, and the child is required when any contributing child definition requires it. A composed table is open only when the effective closure set is empty. Otherwise it is closed against exactly that set.
For a composed collection, the effective closure set retains precedence
over dynamic entries in the same way a locally declared fixed child does. Every
remaining dynamic entry MUST satisfy every contributed itemtype, and its key
MUST satisfy every contributed keypattern. A collection's required
dynamic-entry constraint may therefore be supplied entirely by one or more
allof components; it need not repeat a local itemtype.
Whether a composed node may be absent is governed by optional on the local
definition, as Optionality defines.
Determinate Fixed-Child Set
The determinate fixed-child set of a definition is computed at schema-load time, without reference to any document. It names exactly the children the definition is known to have whatever value is later validated against it. It is the union of:
- the names of the definition's own nested child definitions, including those
written through the reserved
childrennamespace; - the determinate fixed-child set of the definition named by
type, when the selector is a reference to a reusable definition, followed through a chain of such references; and - the determinate fixed-child set of every
allofcomponent.
The computation is therefore recursive: a component that itself composes with
allof contributes everything its own composition contributes determinately.
A oneof or anyof selector and an if/then/else selector contribute
nothing to this set, and contribute no itemtype and no keypattern
either. Their alternatives and branches may declare different children, and
which one applies is not knowable until a document value exists, so no
schema-load rule may depend on that choice. The restriction is a property of the
selector, not of the whole definition: a union or conditional definition that
also declares allof still contributes whatever those components contribute
determinately.
Entries written through the reserved children namespace are ordinary fixed
children. As described under
Quoted and Special Keys, children is a syntactic
escape for target keys whose names collide with schema property names, not a
different kind of child; both spellings denote the same target key and validate
identically. Such entries therefore belong to both sets defined here, take part
in requiredness and unknown-key handling like any other fixed child, and are
valid sibling-rule operands. The operand is the target key name, which is the
path segment following children, never the literal string children.
The determinate set is the set every schema-load rule reads, and it is the only one such a rule may read, because no document exists at schema-load time. Two rules read it:
- Sibling-rule operand resolution and
exactlyoneapplicability, as required under Sibling Presence Rules. - The collection
itemtyperequirement, as required under Collection of Elements for Dynamic Keys.
Validators MUST NOT use the determinate set to reject unknown document keys. Load-time determinacy and validation-time closure are different questions: a child no schema-load rule may assume exists is still an ordinary child of a document node whose alternative or branch declares it. Unknown-key rejection reads the effective closure set defined next.
Effective Closure Set
The effective closure set of a document node is computed at validation time, for that one node. It is the determinate fixed-child set of the node's definition plus the fixed children declared by whichever union alternative or conditional branch was selected for that node:
- When the node's definition, or any
allofcomponent of it, usesoneoforanyof, the alternative being evaluated contributes its own effective closure set for that node.oneofandanyoftry each alternative as a candidate, so every candidate is judged against the set that candidate itself contributes. - When the node's definition, or any
allofcomponent of it, uses the conditional triple, the branch the condition selected contributes its own effective closure set for that node. The branch that was not selected contributes nothing.
Validators MUST judge unknown keys against the effective closure set, and MUST apply requiredness and the value validation of each present child against the same set. A key contributed by the alternative or branch that actually matched is a known key and MUST NOT be reported as an unexpected key, and a child that alternative or branch requires MUST be present. Judging unknown keys against the determinate set instead is incorrect: it rejects the composition patterns this language exists to express, because a composed conditional or variant table would reject every key its matched shape declares.
Dynamic-entry rules are not contributed this way. An alternative or branch that
is itself a collection applies its own itemtype and keypattern while it
validates the node, and the node's effective closure set keeps its precedence
over dynamic entries under every participant, exactly as
Merging by TOML Kind describes for a composed
collection.
When no alternative matches, a key that belongs to the effective closure set of
no candidate could not have been accepted by any of them. An implementation MAY
report such a key as an unknown-key error on the node in addition to, or
instead of, the union's arity failure, as permitted under
Aggregation, Ordering, and Branch Diagnostics.
Openness is likewise decided from the effective closure set. A table is open for
a document node only when that node's effective closure set is empty, and is
closed against exactly that set otherwise. It follows that a union alternative
that is itself an open table does not re-open a node that another participant
has closed: the open alternative contributes no name, so the node's effective
closure set under that alternative is just the determinate set, and a key
outside it makes the alternative fail rather than being accepted as arbitrary
data. When no other participant contributes a fixed child — a standalone oneof
over an open table and a closed table, for instance — the effective closure set
under the open alternative is empty, and that alternative accepts any table
exactly as it would if it were referenced directly.
Worked example. types.database selects a table shape from an engine
discriminator and contributes id through its own allof, and
elements.composed composes the whole conditional definition:
[types.common]
type = "table"
[types.common.id]
type = "integer"
[types.sqliteDatabase]
type = "table"
[types.sqliteDatabase.engine]
type = "string"
[types.sqliteDatabase.file]
type = "string"
[types.serverDatabase]
type = "table"
[types.serverDatabase.engine]
type = "string"
[types.serverDatabase.host]
type = "string"
[types.database]
if = { key = "engine", equals = "sqlite" }
then = "types.sqliteDatabase"
else = "types.serverDatabase"
allof = [ "types.common" ]
[elements.composed]
type = "table"
allof = [ "types.database" ]
The determinate fixed-child set of elements.composed is { id }. The local
definition declares no child, and the types.database component contributes
only what its own allof contributes, because its conditional selector
contributes nothing. A sibling rule written on elements.composed could
therefore name id and no other child.
| Document value | Effective closure set | Result |
|---|---|---|
{ id = 2, engine = "postgresql", host = "db.internal" } |
{ id, engine, host } |
valid |
{ id = 2, engine = "sqlite", file = "db.sqlite" } |
{ id, engine, file } |
valid |
{ id = 2, engine = "postgresql", host = "db.internal", bogus = true } |
{ id, engine, host } |
invalid: bogus is an unknown key |
{ id = 2, engine = "sqlite", host = "db.internal" } |
{ id, engine, file } |
invalid: file is required and host is an unknown key |
host is a known key in the first row because the condition selected
types.serverDatabase, and an unknown key in the last row because the same
document key selected types.sqliteDatabase instead. Nothing about host is
determinate, and nothing about it needs to be; only schema-load rules need
determinacy.
The same reading applies to a variant table composed from a base and a union:
[types.base]
type = "table"
[types.base.id]
type = "integer"
[types.named]
type = "table"
[types.named.name]
type = "string"
[types.labelled]
type = "table"
[types.labelled.label]
type = "string"
[types.identity]
oneof = [ "types.named", "types.labelled" ]
[elements.item]
type = "table"
allof = [ "types.base", "types.identity" ]
| Document value | Result |
|---|---|
{ id = 1, name = "a" } |
valid: types.named matched and contributes name |
{ id = 1, label = "a" } |
valid: types.labelled matched and contributes label |
{ id = 1, name = "a", label = "b" } |
invalid: both alternatives fail on the other's key, so oneof matches none |
{ id = 1, other = true } |
invalid: no alternative contributes other, so no alternative matches |
{ name = "a" } |
invalid: id is required |
The determinate fixed-child set here is { id }, so id is the only name an
exactlyone, mutuallyexclusive, or dependentrequired rule on
elements.item could use, while name and label are ordinary known keys of
whichever document node their alternative matched.
Composition Examples
Two components with disjoint fixed children. Both fixed-child sets are
{ x, y }, both children are required, and the composed table is closed
against exactly those two names:
[types.hasX]
type = "table"
[types.hasX.x]
type = "integer"
[types.hasY]
type = "table"
[types.hasY.y]
type = "integer"
[types.point]
type = "table"
allof = [ "types.hasX", "types.hasY" ]
| Document value | Result |
|---|---|
{ x = 1, y = 2 } |
valid |
{ x = 1 } |
invalid: y is required |
{ x = 1, y = 2, z = 3 } |
invalid: z is an unknown key |
Two components that overlap on one child. The effective definition has a single
name child whose value must satisfy both contributing definitions, and name
is required because one contributor requires it:
[types.namedThing]
type = "table"
[types.namedThing.name]
type = "string"
minlength = 3
[types.slugNamed]
type = "table"
[types.slugNamed.name]
type = "string"
pattern = "^[a-z]+$"
optional = true
[types.entity]
type = "table"
allof = [ "types.namedThing", "types.slugNamed" ]
| Document value | Result |
|---|---|
{ name = "alpha" } |
valid: satisfies minlength and pattern |
{ name = "Alpha" } |
invalid: fails the contributed pattern |
{ name = "ab" } |
invalid: fails the contributed minlength |
{ } |
invalid: name is required by types.namedThing |
A collection whose fixed child and whose dynamic-entry rules come from
different participants. schemaVersion is contributed locally, while
itemtype and keypattern are contributed by the component and apply only to
the remaining dynamic entries:
[types.featureFlags]
type = "collection"
itemtype = "boolean"
keypattern = "^[a-z][a-z0-9-]*$"
[types.versionedFlags]
type = "collection"
allof = [ "types.featureFlags" ]
[types.versionedFlags.schemaVersion]
type = "integer"
| Document value | Result |
|---|---|
{ schemaVersion = 2, dark-mode = true } |
valid |
{ schemaVersion = 2 } |
valid: a collection needs no dynamic entries |
{ dark-mode = true } |
invalid: schemaVersion is required |
{ schemaVersion = 2, darkMode = true } |
invalid: darkMode fails the contributed keypattern |
{ schemaVersion = 2, dark-mode = 1 } |
invalid: 1 fails the contributed itemtype |
The fixed child escapes both dynamic-entry rules: schemaVersion is an
integer rather than a boolean, and its name does not match the
keypattern, yet it is valid because fixed children take precedence over
dynamic entries.
An allof component may itself contain oneof, anyof, or another allof
when its effective type is unambiguous. A composed definition may be referenced
from type, itemtype, items, oneof, anyof, allof, then, or else. All composition
references MUST resolve at schema-load time, and composition/type-selection
cycles are malformed. Structural recursion that consumes a child or container
member remains valid.
Validation and Data Model
TOML Schema validation does not modify the parsed TOML data model.
A validator MUST NOT mutate, replace, or augment the TOML data object produced by the underlying parser. An API that returns that object MUST return the same parsed keys and values that would exist without schema validation.
Schema loading additionally requires source-shape information for inline-table
properties whose names may also name child definitions. In particular,
default = { ... }, dependentrequired = { ... }, and if = { ... } cannot be
distinguished
from same-named child tables by their logical TOML values alone. A schema loader
MUST use a parser that preserves key/value-versus-table syntax or exposes enough
source-position information to recover it; a logical-value-only TOML API is
insufficient for this distinction. Loaders MUST NOT guess from inline-table
member names.
Parsed Value Equality
TOML Schema uses one equality relation for allowedvalues membership,
uniqueitems, and comparison of defaults contributed by composition. Equality
is defined over parsed TOML values:
- strings and booleans compare by value, without Unicode normalization;
- numeric values compare by mathematical value, so integer
1equals float1.0, positive and negative zero are equal, and NaN equals NaN only for this equality relation; - integer comparison remains exact across the full TOML signed 64-bit range, including comparison with a float, and implementations MUST NOT first round the integer to the float's binary format;
- temporal values require the same TOML temporal type and the same parsed
fields, including the numeric UTC offset for an offset date-time; equivalent
spellings such as
.1and.100orZand+00:00are equal, but offset date-times with different retained local fields or offsets are unequal even when they identify the same instant; - arrays compare by length and equal values at every index; and
- tables compare by their unordered parsed key sets and recursively equal values. Source key order, table syntax, and lexical spelling do not participate.
This equality relation is distinct from the ordering used by min and max.
In particular, offset date-times that identify the same instant compare equal
for a range boundary but remain unequal here when their retained local fields
or offsets differ. Implementations MUST NOT reuse instant ordering as
allowedvalues, uniqueitems, or default-comparison equality.
Expressiveness and Validation Scope
This section is informative. It records what version 1.0 deliberately does and does not model; every rule it alludes to is stated normatively in the section it cites.
TOML Schema describes the parsed TOML value tree. It can model the structural patterns used by major configuration formats, including:
- fixed keys and closed tables;
- open tables for extension-owned data;
- dynamic-key maps with uniform values by using
collection; - open namespaces with specially typed well-known keys by combining a
collectionwith fixed child definitions; - arrays of tables and arrays of inline tables by using a named table as an
array's
itemtype; - scalar-or-table and other alternative representations with
oneoforanyof; - table shapes selected from a direct child's value with
if,then, andelse; - standardized email, UUID, URI, host-name, and IP-address strings with
format; - single-table-or-array-of-table representations through named container alternatives;
- fixed-length heterogeneous arrays with
items; and - quoted, empty, or literal dotted keys through normal TOML key syntax.
The checked-in schemas under examples/ exercise these patterns
against formats including Cargo manifests,
Python pyproject.toml,
Hugo, Netlify, GitLab Runner, and Cloudflare Wrangler.
Version 1.0 includes direct-sibling value conditionals, direct-sibling presence dependencies, at-most-one and exactly-one groups, conjunctive reusable composition, whole-item array uniqueness, defaults, and deprecation annotations. These features remain deliberately bounded. Version 1.0 does not define keywords for:
- following arbitrary document paths or comparing values at different paths;
- making a field absent precisely when its name appears in another array;
- selecting array uniqueness by one field rather than the complete item value;
- materializing defaults into parsed TOML data;
- overriding a constraint or modeling an application's runtime inheritance and merge precedence;
- importing, including, or otherwise referencing a definition in another schema document;
- closing a
collectionagainst unknown dynamic keys; or - opening or dynamically keying the document root.
The last three are capabilities a reader may reasonably expect, so they are stated explicitly rather than left to inference.
No cross-file composition. Version 1.0 defines no import, include, or
cross-document reference mechanism. Every named reference resolves within the
[types] table of the same schema document, as
Types Table requires, and the location metadata
described under
TOML Reference of a TOML Schema binds one
TOML document to one schema document rather than assembling several. A shared
vocabulary must therefore be copied into each schema that needs it. This is a
deliberate boundary for 1.0: a cross-document reference mechanism also requires
retrieval, identity, caching, and trust rules for the referenced documents, and
those are out of scope for this version.
No closed collection. A collection is open to dynamic keys by
construction, as Collection of Elements for Dynamic Keys
states. keypattern constrains the shape of a dynamic key and itemtype
constrains its value, but no keyword fixes a collection's key set the way a
table with fixed children is closed. A schema that needs both dynamic entries
and a bounded key set must either enumerate the keys as fixed children of a
table, which gives up dynamic keying, or make the dynamic-entry rule
unsatisfiable so that only the fixed children remain acceptable. The companion
toml-schema.tosd uses the second technique, referencing a
types.never definition as an itemtype to emulate a closed collection. That
idiom works, but it is a workaround for a missing capability rather than a
language feature; the recommendation that follows from it is stated under
Collection of Elements for Dynamic Keys.
No open or dynamically keyed root. The [elements] table is a closed,
fixed-key table, as Elements Table requires. No
schema property applies to [elements] itself, so the root cannot be declared a
collection, cannot carry a keypattern or an itemtype, and cannot be left
open to arbitrary top-level keys. A document format whose top-level keys are
user-chosen cannot be described at the root in version 1.0; it can only be
described one level down, by declaring a top-level key whose definition is a
collection.
For example, a schema can choose a database configuration shape from a sibling
engine value. It still cannot compare values in different tables or follow an
arbitrary path from one location to another. A pyproject.toml schema can
require project.dynamic entries to be unique, but cannot require a field to
be absent precisely when its name appears in that array. Those cross-path
application policies require an additional semantic-validation pass.
Some configuration sections intentionally combine known fields with arbitrary
future or extension-owned fields. A collection with fixed child definitions
and itemtype = "any" models that forward-compatible shape, but unknown keys
cannot then be distinguished from misspellings; the recommendation that follows
from that trade-off is stated under
Collection of Elements for Dynamic Keys.
Version 1.0 defines default and deprecated; examples and editor-specific
presentation hints remain outside the standard vocabulary.
Validation applies to parsed keys and values, not to their lexical spelling. A schema cannot require dotted-key notation instead of table headers, an inline table instead of a regular table, or a particular quoting, whitespace, or comment style when those forms produce the same TOML value tree.
Evaluation
This section defines the normative evaluation model: when each decision is made, how a type reference resolves to a definition, which reference cycles are legal, what validating one node against one definition produces, and how those results combine. It uses the vocabulary fixed under Terminology and the diagnostic shape and codes fixed under Diagnostics.
The model is descriptive of required outcomes, not of implementation structure. An implementation MAY use any strategy — eager, lazy, memoized, compiled — provided it produces the results this section requires.
Evaluation Phases
TOML Schema evaluation has exactly two phases. Every rule in this specification
belongs to one of them, and the phase a rule belongs to is normative because it
determines what information that rule is permitted to read. Schema discovery, the
optional step that locates a schema from a document's [toml-schema] table,
precedes both and is described under
TOML Reference of a TOML Schema.
Schema-Load Phase
The schema-load phase reads one TOML Schema document and nothing else. No document being validated exists, and no rule in this phase may assume one. The phase either produces a loaded schema or fails.
A schema loader MUST perform the following, and each step MUST be complete before the steps that depend on it begin:
- Parse. Parse the schema document as TOML, preserving key/value-versus-table source shape as required under Validation and Data Model.
- Document shape. Verify the top-level structure and reject any other top-level table or key/value pair, as required under Top-Level Structure Conditions.
- Language version. Verify
[toml-schema].versionas required under Schema Versioning. A schema whose version is unsupported MUST fail to load before any keyword is interpreted, because later steps depend on the recognized keyword set for that version. - Keyword closure and applicability. Reject any key/value pair inside a
schema definition whose name is not a defined schema property, reject a
recognized property applied to a definition that does not permit it, and reject
combinations excluded by
Schema Definition Properties. This includes
selector exclusivity: at most one of
type,oneof,anyof, and the conditional triple, and at least one unless the definition is an implicit table or a pure mixin, both described under Selectors. It also includes the exclusion betweenitemsand the per-member value-constraint subset and the rejection of a per-member constraint that is also declared on the definition itsitemtyperesolves to. Every check in this step is local unless Local and Effective Checks lists it as effective. - Reference resolution. Resolve every type reference in the document, as defined under Reference Resolution. Every reference MUST resolve, including references inside definitions that are optional or that no document will exercise.
- Cycle classification. Build the reference graph and reject illegal cycles, as defined under The Reference Graph. This step MUST precede every step that traverses references, so that those traversals terminate.
- Determinate analysis. Compute the
determinate fixed-child set of every
definition, and apply every rule that reads it: sibling-rule operand resolution
and
exactlyoneapplicability under Sibling Presence Rules, and the collectionitemtyperequirement under Collection of Elements for Dynamic Keys. - Composition determinacy. Verify that every
allofcomposition agrees on effective type, that no component is named twice, that no local union or conditional selector combined withallofis indeterminate, and that every pure mixin resolves to exactly one TOML kind, as required under Conjunctive Composition and Composition Supplying the Local Skeleton. - Single-definition consistency. Apply the remaining schema-load consistency
checks:
allowedvaluesentry kinds and their agreement with sibling constraints,minnot greater thanmax,minlengthnot greater thanmaxlength, validformatnames, compilable in-profilepatternandkeypatternvalues as required under Pattern, and the validity of every declareddefaultagainst its full effective definition as required under Default.
A schema-load failure is not a document diagnostic. When loading fails, a validator MUST NOT validate any document against that schema and MUST NOT report the failure as a document diagnostic; the phase distinction is normative under Diagnostics.
A loaded schema is independent of any document. An implementation MAY cache and reuse it, and validating a document MUST NOT change it.
Document-Validation Phase
The document-validation phase applies one loaded schema to one parsed TOML document. Only this phase may read document values.
Validation MUST be deterministic: the same loaded schema and the same parsed document MUST produce the same validity and the same set of errors, independent of document key order, of the order in which alternatives are evaluated, and of evaluation strategy. Validation MUST NOT modify the parsed document, as required under Validation and Data Model.
Validation begins with the document root. The root node is the parsed document's
root table, and its definition is [elements], applied as a closed table under
the rules in Elements Table. The reserved root
[toml-schema] table is excluded from application-data validation unless
[elements.toml-schema] is declared, as described under
TOML Reference of a TOML Schema.
A validator MUST NOT report a schema-load error during this phase. If a condition that the schema-load phase is required to reject is discovered while validating, the implementation's loader is non-conforming; a validator MUST NOT convert such a condition into a document error.
Reference Resolution
A type reference is a string. Resolution maps it either to a built-in type or to
one reusable definition in [types], and it is performed entirely in the
schema-load phase.
Resolution Algorithm
Given a reference string R appearing in type, itemtype, items, oneof,
anyof, allof, then, or else, a schema loader MUST resolve it by the
following ordered steps. The first step that produces a result or an error
decides the outcome.
- Normalize. If
Rbegins with the literal characterstypes., remove that prefix exactly once. Call the resultN. OtherwiseNisR. The prefix is never removed twice: the reference"types.types.x"normalizes totypes.x, which no reusable type may be named, so it fails at step 4. - Built-in names. If
Nis one of the built-in type namesany,string,integer,float,boolean,offset-date-time,local-date-time,local-date,local-time,array,table, orcollection, thenRdenotes that built-in type. Built-in names take precedence over any other interpretation. - Context restrictions on built-ins. If step 2 selected
anyorcollection, apply the context restrictions defined under Type Reference Restrictions. These restrictions are applied to the normalized name, so"types.any"and"types.collection"are restricted exactly as the bare spellings are.thenandelseaccept no built-in at all, so any reference reaching step 2 from those properties is an error. - Named definitions. Otherwise
NMUST name a direct child of[types], compared against the exact decoded TOML key of that child.Nis a whole name, never a path: a dot inNis an ordinary character, so"types.network.endpoint"and"network.endpoint"both name the single definition[types."network.endpoint"]. - Unresolved. If no such direct child exists, the reference is unresolved and
the schema loader MUST reject the schema with
unresolved-reference.
Two references are the same reference when they normalize to the same N.
This is the identity used by the duplicate-component rule under
Conjunctive Composition and the
distinct-alternative rule under
Alternative Types.
Built-in Names Are Reserved
Because normalization happens before lookup and built-in names are tested first,
type = "types.string" denotes the built-in string. It is a redundant but valid
spelling of type = "string", and a schema loader MUST NOT reject it. This is the
only defensible resolution: Types Table already forbids a
reusable definition from being named string, so the competing reading — treat
types.string as a lookup for a user-defined type named string — could never
resolve to anything and would make a valid schema fail for no expressible reason.
Because the resolved reference is a built-in and not a named reference, the
sibling-property restriction that
Type Reference places on named references does not apply to it.
Authors SHOULD spell a built-in bare. The types. prefix exists to make a
reference to a reusable definition visually unambiguous; it grants no separate
namespace and cannot be used to reach a user-defined type whose name collides with
a built-in, because no such type can be declared.
[types.port]
type = "integer"
min = 1
max = 65535
[elements.retries]
type = "types.integer" # valid; identical to type = "integer"
[elements.port]
type = "types.port"
The following is invalid, because a reusable definition MUST NOT take a reserved built-in name:
[types.string] # invalid: reserved built-in name
type = "string"
The Reference Graph
The reference graph of a loaded schema has one vertex per definition — every
direct or nested child of [types] and of [elements] — and one edge per
reference from a definition to the definition or built-in it names. Built-in
types are terminal vertices with no outgoing edges.
Consuming and Non-Consuming Edges
Every edge is exactly one of two kinds. The distinction is whether following the edge requires descending into a nested document value.
A non-consuming edge is traversed immediately, against the same document node. Determining the node's effective definition requires following it. Non-consuming edges are:
type, when it names a reusable definition (aliasing);- each entry of
oneofand ofanyof; thenandelse;- each component of
allof.
A consuming edge is traversed only after descending to a nested document node, and that node may or may not exist. Traversal is therefore deferred until a document value is present. Consuming edges are:
- a nested child definition of a
tableorcollection, including one written through the reservedchildrennamespace; itemtypeon anarray, applied to each item;itemtypeon acollection, applied to each dynamic entry;- each entry of
items, applied to one array position.
Cycle Legality
A cycle in the reference graph is legal or illegal according to the edges it contains, and a schema loader MUST decide this at schema-load time.
- A cycle composed entirely of non-consuming edges is unsatisfiable: computing
the effective definition of any vertex on it requires computing its own
effective definition first, and no document value is consumed to make progress.
Schema loaders MUST reject it with
cyclic-reference. - A cycle that passes through at least one consuming edge is legal. It describes a recursive document structure: each traversal of the cycle requires one more level of nesting in the document, and a finite document terminates the recursion.
The rule for non-consuming cycles is deliberately conservative for oneof and
anyof: a cycle through a union alternative is rejected even when a sibling
alternative is terminal. Selecting the alternative to try is a schema-structural
decision, and a self-referential alternative offers no document value to consume
when it is tried.
A legal recursive structure. The cycle from types.node back to types.node
passes through the consuming edge itemtype, so each level of recursion consumes
one more array item:
[types.node]
type = "table"
[types.node.name]
type = "string"
[types.node.subnodes]
type = "array"
itemtype = "types.node"
optional = true
[elements.tree]
type = "types.node"
An illegal cycle. Both edges are type aliases, so neither definition can ever be
reduced to a concrete one, and a schema loader MUST reject this schema:
[types.a]
type = "types.b" # invalid
[types.b]
type = "types.a" # invalid
The same rejection applies to a cycle through oneof, anyof, then, else,
allof, or any mixture of them, and to a self-edge such as [types.a] with
type = "types.a".
The Validation Contract
Results
Validating one node against one definition produces a result with four parts:
- a validity verdict, valid or invalid;
- errors, zero or more diagnostics that determine invalidity;
- warnings, zero or more diagnostics that do not affect validity; and
- annotations, zero or more non-diagnostic values that an implementation MAY
surface, such as the effective
defaultof a slot.
A result is valid when its error list is empty and invalid otherwise. Diagnostics carry the phase, severity, code, instance path, and schema path required by Diagnostics.
Results combine associatively: the combination is valid when every combined result is valid, and its errors, warnings, and annotations are the concatenation of the combined ones, deduplicated under the identity Diagnostics defines. Every rule below that says results combine means this operation.
The Unit of Validation
The unit of validation is one node — one parsed TOML value at one instance
path — paired with one effective definition. It is written
validate(node, definition).
validate is defined only for a node that exists. Whether a node has to exist is
a property of its slot, not of the node, and is decided by the parent as
described next. Keys are never validated by validate: a key is validated by the
node that contains it, through unknown-key classification for a table and
through keypattern for a collection's dynamic entries.
validate MUST be a pure function of the loaded schema and the node's parsed
value. It MUST NOT depend on the node's position among its siblings, on the order
in which nodes are visited, or on results computed for unrelated nodes.
Slot Evaluation
Before descending into a fixed child, a validator evaluates the slot:
- Determine the slot's effective optionality, inheriting through the named
typereference chain as required under Optionality. - If the corresponding key is absent from the parsed parent and the slot is optional, the slot produces an empty valid result. No assertion, annotation, or deprecation warning is produced for an absent optional slot.
- If the key is absent and the slot is required, the slot produces one
missing-requirederror at the child's instance path and no descent occurs. - If the key is present, the slot's result is
validate(child node, child definition).
Keyword Evaluation Order
For a present node N and definition D, a validator MUST evaluate in the
following order. The order is normative because each group either depends on the
groups before it or governs which diagnostics the groups after it may produce.
Effective definition. Follow
D'stypereference chain and mergeDwith everyallofcomponent into the effective definition, as required under The Effective Definition. A validator MUST NOT validateNseparately againstDand against each component.Kind check. Determine the effective type. When the selector is
typeor the conditional triple,N's TOML kind MUST match the coarse category of that effective type;anyselects no category and imposes no restriction. A mismatch produces exactly onetype-mismatcherror, and the validator MUST NOT evaluate any group below, MUST NOT descend intoN, and MUST NOT apply sibling rules. When the selector isoneoforanyof, the kind check belongs to each alternative and is performed in group 3.Alternative and branch selection. For the conditional triple, evaluate
ifagainstN— the condition is false when the named direct child is absent, and otherwise compares by Parsed Value Equality — and evaluate only the selected branch, as required under Conditional Selection. Foroneofandanyof, evaluate each alternative. In both cases the alternative or branch is evaluated as the effective definition formed from it together with the local definition'sallofcomponents, and its fixed children joinN's effective closure set for that evaluation. Results are combined as required under Alternative and Branch Commit and Discard.Value assertions. Evaluate every assertion that applies to
N's own value:format,pattern,minlength,maxlength,uniqueitems, and, for a scalar or temporal node,minandmax. Every such assertion is evaluated; none gates another, and a failure of one does not suppress another. On anarrayor acollection,format,pattern,min, andmaxare per-member assertions and belong to group 6, whileminlengthandmaxlengthremain assertions on the container itself and are evaluated here.Allowed values. Evaluate
allowedvaluesmembership. Groups 2, 4, and 5 of this list are the general-case statement of the ordering that Allowed Values fixes for enumerations, and that section is authoritative: membership is evaluated after the kind check and after every other applicable assertion, and never instead of either.Structural application. Apply the node's structure and descend:
- table — form the effective closure set, then
evaluate every fixed-child slot as described under
Slot Evaluation and report every document key outside that
set as an
unknown-keyerror, as required under Tables; - collection — classify every document key against the effective closure
set; fixed children take precedence and are evaluated as slots; every
remaining key is a dynamic entry whose key MUST satisfy every applicable
keypatternand whose value MUST validate against every applicableitemtype, as required under Collection of Elements for Dynamic Keys. Every per-memberallowedvalues,min,max,pattern, andformatdeclared on the collection applies to each dynamic entry's value. A collection never produces anunknown-keyerror; - array — with
items, check arity first and then validate each position; withitemtype, validate every item; with neither, items are unconstrained. Per-memberallowedvalues,min,max,pattern, andformatapply to each item.
Each descent is itself a
validatecall and MUST apply groups 1 through 8 to the nested node. Results from descent combine intoN's result.- table — form the effective closure set, then
evaluate every fixed-child slot as described under
Slot Evaluation and report every document key outside that
set as an
Sibling presence rules. Evaluate
dependentrequired,mutuallyexclusive, andexactlyoneagainst the parsed key presence ofN, as required under Sibling Presence Rules. They inspect presence only, so a validator MUST evaluate them regardless of whether the corresponding child values validated, and MUST resolve their operands against the determinate fixed-child set computed at schema-load time.Annotations. If groups 2 through 7 produced no error for
N, emit thedeprecatedwarning when the effective definition is deprecated, and expose the effectivedefaultas an annotation. WhenN's result is invalid, a validator MUST NOT emit its deprecation warning; the warning describes a value that validated, as required under Deprecation. A deprecated parent emits one warning at the parent's instance path and never one per descendant.
Error Reporting Completeness
Validity MUST NOT depend on a validator's reporting strategy. A validator MAY stop at the first error, and it SHOULD report every independent error it can determine, as Aggregation, Ordering, and Branch Diagnostics describes.
Whatever the strategy:
- an invalid document MUST produce at least one error;
- every reported error MUST be an error that complete evaluation would also produce, so a short-circuiting validator reports a subset and never an additional or different error; and
- the mandatory suppressions below are not a reporting strategy. They are normative, and a validator that reports all errors MUST still suppress them, so that two complete implementations report the same errors.
The mandatory suppressions are:
- a kind mismatch suppresses every group below the kind check for that node, as required in group 2 above;
- an absent optional slot produces nothing;
- a missing required slot produces one
missing-requirederror and no descent; - an
unknown-keyerror produces no value validation for that key, because no definition applies to it; - a tuple arity mismatch produces one
tuple-lengtherror; positions present in both the document array anditemsare still validated, and no diagnostic is produced for a position that exists in only one of them; and - diagnostics produced inside a discarded alternative or branch are suppressed entirely, as required under Alternative and Branch Commit and Discard.
Combining Results
allofdoes not combine results. Components are merged into one effective definition before validation, and the node is validated once against that definition. Errors therefore describe the composed shape, never a per-component evaluation. See The Effective Definition.oneofis valid when exactly one alternative's result is valid.anyofis valid when at least one alternative's result is valid. See Alternative and Branch Commit and Discard for what happens to their diagnostics.if/then/elseevaluates exactly one branch, and that branch's result is the node's result for the branch. The branch that was not selected is never evaluated, and the condition itself produces no diagnostic.itemtypecombines the results of every item or dynamic entry. The container is valid only when every member is valid, and member errors carry the member's instance path.itemscombines the arity check with the result of every validated position.collectioncombines the results of its fixed-child slots with the results of its dynamic entries, includingkeypatternfailures on keys.
In every case the container's own assertions and its members' results combine: a node is valid only when its own assertions hold and every node below it is valid.
Alternative and Branch Commit and Discard
One rule governs both a oneof or anyof alternative and a then or else
branch of the conditional triple, so this subsection states it once for both.
An alternative or branch is evaluated speculatively. It is committed when its diagnostics and annotations become part of the node's result, and discarded when they do not.
Speculative evaluation MUST be free of side effects. A discarded alternative or branch MUST NOT contribute to the node's effective closure set, MUST NOT influence how the node's keys are classified for the committed one, and MUST NOT leave any diagnostic in the node's result. A validator that accumulates diagnostics into a shared buffer MUST scope that buffer to the alternative or branch being evaluated.
The rules are:
oneof, exactly one successful alternative. The node is valid. That alternative is committed: its warnings and annotations surface, including itsdeprecatedwarning. Every other alternative is discarded.anyof, at least one successful alternative. The node is valid. Every successful alternative is committed, and their warnings and annotations are deduplicated under the identity Diagnostics defines. A deprecated successful alternative therefore contributes a warning even when another successful alternative is not deprecated. Failed alternatives are discarded. This is the successful-alternative rule stated under Alternative Types, which is authoritative for which annotations an alternative contributes.- No successful alternative. The node is invalid. Every alternative is
discarded, and the validator MUST report the failure on the union node itself:
oneoffor aoneofdefinition andanyoffor ananyofdefinition. A validator MAY attach the discarded alternatives' errors as subordinate explanatory detail of that node-level error — this is often the most useful diagnostic a user can get — but MUST NOT surface them as independent top-level errors, and MUST NOT report any warning or annotation from a failed alternative. oneof, more than one successful alternative. The node is invalid, becauseoneofrequires exactly one match. The validator MUST report the failure on the union node itself with the codeoneof, and every alternative is discarded, so no successful alternative's warning or annotation surfaces.- Conditional triple. No discard occurs. The condition selects one branch, only that branch is evaluated, and its result is committed unconditionally — including its errors when it fails. The unselected branch is never evaluated, so it can produce nothing to discard.
One exception to rule 3 is already defined by this specification and is the only
diagnostic permitted to escape a discarded alternative: when no alternative
matches, a document key that belongs to the effective closure set of no candidate
could not have been accepted by any of them, and an implementation MAY report it
as an unknown-key error on the node in addition to, or instead of, the union's
failure, as permitted under Effective Closure Set. No
other alternative-local or branch-local diagnostic may be promoted this way in
version 1.0.
Because selection also selects the selected definition's fixed children,
discarding is what keeps composition sound. In the following schema, types.named
and types.labelled each declare a key the other rejects:
[types.base]
type = "table"
[types.base.id]
type = "integer"
[types.named]
type = "table"
[types.named.name]
type = "string"
[types.labelled]
type = "table"
[types.labelled.label]
type = "string"
[types.identity]
oneof = [ "types.named", "types.labelled" ]
[elements.item]
type = "table"
allof = [ "types.base", "types.identity" ]
For the document node { id = 1, name = "a" }, the types.labelled alternative
produces an unknown-key error for name and a missing-required error for
label. Both are discarded, because types.named succeeded and committed.
Reporting the discarded alternative's errors would make every valid variant table
look invalid.
For the document node { id = 1, other = true } no alternative succeeds, so the
node is invalid. other belongs to no candidate's effective closure set, so a
validator MAY report it as an unknown-key error under the exception above, in
addition to or instead of the union's failure.
Diagnostics
An implementation produces diagnostics while discovering a schema, while loading a schema, and while validating a document. This section defines the diagnostic record, the two severities, the instance-path and schema-path encodings, aggregation and branch-reporting rules, and the stable code registry. It is authoritative for every diagnostic code, severity, and path this specification names.
Human-readable messages, output formatting, locale, and additional implementation fields are presentation. They are not a conformance target. Implementations MUST NOT be compared, and MUST NOT compare themselves, by message text. Conformance is judged on phase, severity, code, instance path, schema path, and the validity result.
Phases
Every diagnostic belongs to exactly one phase:
- discovery — resolving a schema from a document's
[toml-schema]table, including URI policy and retrieval; - schema-load — parsing a schema document and applying every schema-load rule in this specification, including self-schema validation and reference-aware checks, as enumerated under Schema-Load Phase;
- validation — applying a successfully loaded schema to a TOML document, as described under Document-Validation Phase.
Implementations MUST distinguish the three phases. A discovery or schema-load
error prevents validation from starting: there is no validity result for the
document. A discovery warning does not prevent validation.
resource-limit-exceeded may be produced in any phase; if it is produced during
validation, the document MUST be reported as not successfully validated even when
no constraint had yet failed, as required by
Security Considerations.
TOML parse failures of the document or of the schema source are parser errors, not TOML Schema diagnostics. An implementation SHOULD surface them as such and MUST NOT assign them a registry code.
Diagnostic Record
A diagnostic has these fields:
| Field | Required | Meaning |
|---|---|---|
phase |
MUST | discovery, schema-load, or validation |
severity |
MUST | error or warning |
code |
MUST | a registry code or a namespaced extension code |
instance_path |
MUST for validation; MUST NOT for discovery and schema-load, except resource-limit-exceeded produced during validation |
where in the document the condition was observed |
schema_path |
MUST when the condition is attributable to a location in a schema document | where in the schema the failing rule is declared |
message |
MUST | human-readable text; content is implementation-defined |
An implementation MAY add fields such as source line and column, a keyword name,
or nested causes. Nested causes are presentation. They MUST NOT affect validity
and MUST NOT be counted as additional result diagnostics unless they would
independently satisfy this section as committed diagnostics.
A validation result contains zero or more diagnostics. The document is
valid if and only if the result contains no diagnostic of severity error.
Warnings do not affect validity. Implementations MUST provide separate access to
errors and warnings. A command-line validator MUST exit successfully when the
document is valid, including when the only diagnostics are warnings.
Severity
This version defines two severities.
error. The condition makes the input unusable for the phase that detected it. A discovery or schema-load error means no schema is loaded. A validation error means the document is invalid.
warning. The condition is reported and the phase continues. The only
validation warning defined by this version is deprecated. The only discovery
warning defined by this version is version-mismatch. Schema-load produces no
warnings in this version: a malformed schema is an error.
Normative assignment:
- A present node whose effective definition is deprecated MUST produce a
deprecatedwarning and MUST NOT, by deprecation alone, produce an error. An absent optional slot MUST produce no deprecation warning. A deprecated parent produces one warning at the parent's instance path, not one warning per descendant. These rules are those of Deprecation and Annotations; this section only names the diagnostic. - A referencing document whose
[toml-schema].versiondiffers from the loaded schema's language version without a major-version incompatibility MUST produce aversion-mismatchwarning and MUST NOT, by that mismatch alone, fail discovery or validation, as required by TOML Reference of a TOML Schema. - Every other condition in the registry is an error.
resource-limit-exceededis always an error.
Instance Path
An instance path identifies a node in the parsed TOML document. The grammar is a restricted JSONPath-like dotted form. It is not JSONPath: there are no filters, slices, wildcards, or descendant segments.
- The root document table is
$. - A table or collection child whose decoded key K is appended as
.followed byEncodeKey(K). - An array element at zero-based index i is appended as
[, the decimal digits of i with no sign and no leading zeros except for0itself, and]. There is no dot before[.
EncodeKey(K):
- If K is non-empty and every Unicode scalar value in K is an ASCII letter,
digit,
_, or-, K is written literally. - Otherwise K is written as a JSON string per RFC 8259: surrounded by
", with"and\escaped, withU+0008,U+0009,U+000A,U+000C, andU+000Descaped as\b,\t,\n,\f, and\r, and with every otherU+0000-U+001Fscalar escaped as\u00XX, whereXXis the scalar value in two lowercase hexadecimal digits. Paths are compared as strings, so the digit case is normative:\u001f, never\u001F.
Examples:
| Document node | Instance path |
|---|---|
| the document root | $ |
top-level key host |
$.host |
composed.host |
$.composed.host |
index 0 of direct |
$.direct[0] |
key google.com under site |
$.site."google.com" |
| the empty key at the root | $."" |
key children under parent |
$.parent.children |
The reserved children escape namespace is a schema-document spelling. It is
never a segment of an instance path. A child declared as
[elements.plugin.children.type] is instance path $.plugin.type.
The reserved root [toml-schema] table of a target document is not an
application-data node unless [elements.toml-schema] is present. When it is
ignored during application-data validation, implementations MUST NOT emit instance
paths under $.toml-schema for ordinary unknown-key or requiredness rules.
Instance paths use decoded TOML keys, not lexical spellings. Dotted keys, quoted keys, table headers, and inline tables that produce the same parsed node have the same path, consistent with Parsed Value Equality.
Schema Path
A schema path identifies a node in the parsed schema document with the same
encoding algorithm as an instance path, applied to the schema's value tree, also
starting at $.
Typical prefixes are $.toml-schema, $.elements, and $.types. A schema
property written as a key/value pair on a definition is a final path segment named
after that property. A child definition's path is that child's table. When a child
is written through the reserved children namespace, the schema path includes the
children segment and the instance path does not.
When a single schema property declares the rule that failed, the schema path ends
at that property. When no single property does — because the rule spans several
properties, as a reversed min/max pair does, or because it is structural, as a
closed table's unknown-key rule is — the schema path is the definition table
itself.
Examples:
| Schema location | Schema path |
|---|---|
unrecognized property patttern on elements.port |
$.elements.port.patttern |
reversed bounds on elements.port |
$.elements.port |
min on reusable types.boundedInteger |
$.types.boundedInteger.min |
escaped child type under elements.plugin |
$.elements.plugin.children.type |
oneof array on elements.id |
$.elements.id.oneof |
a value of the wrong kind for type on elements.port |
$.elements.port.type |
an unknown key of closed table elements.item |
$.elements.item |
When a named type reference is applied, the schema path of a failed assertion is
the location at which that assertion is declared, which is the referenced
definition when the constraint lives there, not the use site. The use site appears
in the schema path of failures of use-site annotations such as a local
deprecated or optional.
Discovery diagnostics whose condition is in the target document's [toml-schema]
table use that document as the schema-path tree for this purpose, so a missing
location is $.toml-schema.location. They still MUST NOT set instance_path;
discovery is not document validation.
Aggregation, Ordering, and Branch Diagnostics
Completeness. A validator MAY stop after the first validation error
(fail-fast) or collect further errors. Stopping is a presentation and performance
choice. If it stops, it MUST have emitted at least one error that justifies
invalidity. A validator SHOULD, when it continues, report independent errors at
the same node — every unknown key, every missing required fixed child, and every
failed sibling-presence group — rather than only the first. The mandatory
suppressions under
Error Reporting Completeness still apply.
Resource limits in Security Considerations MAY
truncate the result; truncation MUST itself be reported as
resource-limit-exceeded and MUST NOT be presented as a complete successful
validation.
Schema-load and discovery MAY likewise stop at the first error. They SHOULD report independent property errors on the same definition when doing so is cheap, for example both an unrecognized property and an inapplicable one, but a single schema-load error is sufficient to reject the schema.
Order. Diagnostic order is not significant for conformance. Two
implementations that emit the same set of
(phase, severity, code, instance_path, schema_path) tuples conform even if they
sort differently. Implementations SHOULD emit a stable order: pre-order of the
instance tree, and at a table, document source order when the parser preserves it.
Validity. Presence of any validation error makes the document invalid. Absence of validation errors makes it valid, even when warnings are present.
Unions (oneof, anyof).
Alternative and Branch Commit and Discard
defines which alternatives are committed and which are discarded. This section
requires:
- Diagnostics produced while evaluating a discarded alternative MUST NOT appear in the validation result. This includes both errors and warnings. A naive validator that surfaces every alternative's inner failures floods the user with constraints that were never meant to apply and is not conforming.
- When
oneofmatches no alternative, or matches more than one, the validator MUST emit one error at the node's instance path with codeoneof. Whenanyofmatches no alternative, it MUST emit one error at the node's instance path with codeanyof. Inner diagnostics of the unsuccessful alternatives stay discarded. - When
oneofmatches exactly one alternative, that alternative is committed: its warnings are result diagnostics. A successful match has no errors. - When
anyofmatches one or more alternatives, every successful alternative is committed for annotation purposes, and the warnings of every successful alternative are result diagnostics. - An implementation MAY attach discarded diagnostics as nested
causeson the union-level error. Nested causes are presentation. - As already permitted under
Effective Closure Set, when no alternative matches, an
implementation MAY also emit
unknown-keyfor a document key that belongs to no candidate's effective closure set, in addition to the union-level error. It MUST NOT emitunknown-keyfor a key that some candidate would have accepted.
Conditionals (if / then / else). The condition itself emits no validation
diagnostic. Only the selected branch is committed. Diagnostics of the unselected
branch MUST NOT appear in the result. There is no wrapper conditional validation
code: a selected branch that rejects the node produces that branch's ordinary
codes (type-mismatch, unknown-key, missing-required, constraint codes, and
so on) as if the branch were the node's definition.
allof. Validation is against the effective definition, not against each
component in isolation. Mixin composition MUST NOT produce unknown-key errors
for fixed children contributed by other participants. When several participants
contribute assertions that independently reject the same value, each assertion MAY
produce a diagnostic at its own schema path. Diagnostics that share code, instance
path, and schema path MUST be emitted once.
Deduplication. The identity of a diagnostic for deduplication is
(code, instance_path, schema_path). Message text does not participate.
Implementations MUST drop duplicates under this identity, including duplicate
deprecated warnings contributed by multiple successful anyof alternatives or
multiple allof participants. An absent path field compares equal to another
absent path field.
Extensibility
Codes in the registry are lowercase ASCII letters, digits, and hyphens, and they
do not contain .. This version's set is closed.
A future MINOR or PATCH revision of this specification MAY add codes in that unprefixed namespace. Implementations MUST treat an unrecognized unprefixed code emitted by a newer validator as an unknown standard code, not as an error in the diagnostic itself.
Implementations MAY emit additional codes for implementation-specific checks.
Every such code MUST begin with x-, followed by an implementation token of
[a-z][a-z0-9]*, a hyphen, and a name, for example x-tosd-internal. Extension
codes MUST NOT collide with the unprefixed registry. Consumers that do not
recognize an extension code MUST still honor its severity for validity and exit
status.
Implementations MUST NOT emit an unprefixed code that is not in this registry.
Command-Line Exit Status
A command-line validator MUST use distinct exit statuses for the three outcomes that callers need to tell apart:
- 0 — a schema was loaded and the document is valid (warnings permitted);
- 1 — a schema was loaded and the document is invalid;
- 2 — discovery or schema-load failed, or the invocation itself was unusable (usage, missing files, TOML parse failure of the schema or of the document under validation).
The canonical tosd CLI uses this mapping. Other command-line validators SHOULD
match it. Library APIs have no exit status; they MUST still distinguish the
phases.
Code Registry
Each code denotes one condition. Several document nodes may produce several diagnostics with the same code. The message an implementation prints is not specified here; the condition is.
Constraint failures are subdivided by schema property, not collapsed into a
single generic constraint code. One catch-all would be cheaper to implement and
useless to consume: an editor cannot highlight min versus pattern, and a CI
filter cannot ignore one family of failures. Per-property codes match the schema
property names and keep the registry small. min and max remain distinct
because "too small" and "too large" are different user actions; likewise
minlength and maxlength. There is no additional umbrella code, and an
implementation MUST NOT substitute one.
There is no itemtype validation code. A collection or array entry that fails its
item definition produces that definition's ordinary codes at the entry's instance
path. Collections never produce unknown-key; an unacceptable dynamic entry is a
keypattern failure or an item-definition failure, as required under
Collection of Elements for Dynamic Keys.
There is no conditional validation code and no allof validation code, for the
reasons given under
Aggregation, Ordering, and Branch Diagnostics.
Composition failures that make a schema malformed are schema-load codes; document
failures against the effective definition use the ordinary validation codes.
Discovery Codes
| Code | Severity | Condition |
|---|---|---|
discovery-missing-location |
error | Discovery was requested and the document has no usable [toml-schema].location. |
discovery-invalid-metadata |
error | [toml-schema] in the target document violates the scalar-only restriction, carries a malformed version or location, or is otherwise not schema-reference metadata. |
discovery-unresolved-location |
error | A relative location cannot be resolved because the document has no base URI, or the resolved reference is not a usable URI. |
schema-retrieval-refused |
error | Retrieval is disabled, or policy rejects the scheme, target, redirect, credentials, or filesystem path. Defined under Schema Discovery and Retrieval. |
schema-retrieval-failed |
error | An authorized retrieval does not yield a usable schema, including transport failure, redirect exhaustion, or an unusable response. Defined under Schema Discovery and Retrieval. |
version-mismatch |
warning | The document's [toml-schema].version and the loaded schema's language version differ without a major-version incompatibility. |
unsupported-version |
error | The document requests, or the schema declares, a language version the implementation must reject (unsupported major, greater minor, or 0.y.z). Also a schema-load code when no discovery is involved. |
resource-limit-exceeded |
error | A configured limit was reached during discovery, for example a retrieval size or redirect budget. Also a schema-load and validation code. |
Schema-Load Codes
| Code | Severity | Condition |
|---|---|---|
unrecognized-property |
error | A key/value pair written directly inside a schema definition is not one of the closed property set for this language version, for example patttern. |
inapplicable-property |
error | A recognized property appears on a definition to which this specification does not permit it, for example pattern on integer, keypattern on table, minlength on boolean, or a kind-specific constraint beside a named type reference. |
exclusive-properties |
error | Two properties that this specification makes mutually exclusive appear together: the selectors type, oneof, anyof, and if; items with itemtype, minlength, maxlength, or a member of the per-member value-constraint subset; the same per-member value constraint declared both inline on an array or collection and on the definition its itemtype resolves to; if.equals with if.in; or an incomplete if/then/else triple. |
unresolved-reference |
error | A type reference in type, itemtype, items, oneof, anyof, allof, then, or else does not name a built-in type or a definition in [types], as defined under Reference Resolution. |
duplicate-reference |
error | Two entries of oneof, anyof, or allof have the same resolved identity after the optional types. prefix is stripped, for example "types.foo" and "foo". |
inverted-range |
error | min is greater than max, or minlength is greater than maxlength, under the ordering this specification defines for that pair. |
invalid-boundary |
error | A min or max value is not a valid boundary for the comparable kind: NaN, a temporal value of the wrong TOML temporal type, a non-numeric boundary on a numeric kind, or an infinite bound on an integer kind. |
indeterminate-operand |
error | A dependentrequired, mutuallyexclusive, or exactlyone operand does not name a member of the definition's determinate fixed-child set. |
invalid-pattern |
error | A pattern or keypattern cannot be compiled under the active profile, as required under Pattern. |
unsupported-pattern |
error | A pattern or keypattern uses syntax outside the portable profile without an explicitly enabled extension profile, as required under Pattern. |
cyclic-reference |
error | The reference graph contains a cycle classified as illegal under Cycle Legality. Structural recursion through a consuming edge is not this code. |
incompatible-composition |
error | allof participants do not agree on effective type, a multi-kind local union is combined with allof, a table is composed with a collection, or a pure mixin has no single determinate effective type. |
invalid-default |
error | A declared default does not validate against the full effective definition at schema-load time, or allof participants contribute unequal defaults with no local default. |
unsupported-version |
error | [toml-schema].version is missing, is not a SemVer string, is not a TOML Schema language version, or is not supported by the implementation. |
schema-malformed |
error | Any other schema-load failure required by this specification: missing [toml-schema] or [elements], an empty oneof, anyof, allof, allowedvalues, or items array, a reserved type name, an invalid children escape entry, a non-boolean deprecated, and similar. Implementations SHOULD prefer a more specific code from this table when one applies. |
resource-limit-exceeded |
error | A configured limit was reached while loading. Also a discovery and validation code. |
Validation Codes
| Code | Severity | Condition |
|---|---|---|
unknown-key |
error | A document key of a closed table, including the document root, is not in the node's effective closure set. Never produced for a collection dynamic entry. |
missing-required |
error | A fixed child that is not optional is absent. The instance path is the missing child's path even though no node is there. |
type-mismatch |
error | The node's TOML kind does not match the coarse category of the effective type, including an array or collection entry whose value is not the item definition's kind. |
allowedvalues |
error | The node's parsed value is not a member of allowedvalues under Parsed Value Equality. For a per-member enumeration the instance path is the member that failed, $.arr[i] or $.coll.key. |
pattern |
error | A string does not match pattern. For a per-member pattern the instance path is the member that failed, $.arr[i] or $.coll.key. |
format |
error | A string does not match the required format. For a per-member format the instance path is the member that failed, $.arr[i] or $.coll.key. |
min |
error | A comparable value, or a comparable array item or collection entry, is less than min, or is NaN. The instance path is the value that failed, so an array item is $.arr[i] and a collection entry is $.coll.key. |
max |
error | A comparable value, or a comparable array item or collection entry, is greater than max, or is NaN. |
minlength |
error | A string, array, or collection is shorter than minlength. For a collection, length counts dynamic entries only. |
maxlength |
error | A string, array, or collection is longer than maxlength. |
uniqueitems |
error | An array with uniqueitems = true contains two items equal under Parsed Value Equality. The instance path is the later duplicate, $.arr[i]. |
tuple-length |
error | An array validated by items does not have exactly that arity. The instance path is the array, not an index. Positions present in both the array and items are still validated and report their own codes at $.arr[i]; a position present in only one of them produces no additional diagnostic. |
keypattern |
error | A collection's dynamic entry key does not match keypattern. The instance path is the entry, $.collection.key. Fixed children are not subject to keypattern. |
oneof |
error | The number of matching oneof alternatives is not exactly one, that is zero matches or more than one. The instance path is the node whose type is being selected. |
anyof |
error | No anyof alternative matches. |
dependentrequired |
error | A trigger child is present and a listed dependent child is absent. The instance path is the missing dependent. |
mutuallyexclusive |
error | More than one member of a mutuallyexclusive group is present. The instance path is the parent table or collection. |
exactlyone |
error | An exactlyone group does not have exactly one present member. The instance path is the parent table or collection. |
deprecated |
warning | A present node is deprecated. An absent optional slot does not produce this code. |
resource-limit-exceeded |
error | A configured limit was reached during validation. The document is not successfully validated. |
Informative Examples
Unrecognized schema property, schema-load:
phase: schema-load
severity: error
code: unrecognized-property
schema_path: $.elements.port.patttern
message: (implementation-defined)
Closed-table misspelling, validation:
phase: validation
severity: error
code: unknown-key
instance_path: $.item.bogus
schema_path: $.elements.item
Deprecated present value, validation:
phase: validation
severity: warning
code: deprecated
instance_path: $.legacy-timeout
schema_path: $.elements.legacy-timeout.deprecated
These examples are not a serialization format. Implementations MAY print diagnostics as text, JSON, or API objects, provided the normative fields are available.
Schema Self-Validation
The companion toml-schema.tosd is a TOML Schema document
that recursively validates schema-definition tables, including the schema
document itself. It models schema properties as fixed children and ordinary
target child definitions as dynamic collection entries. The
children namespace resolves the remaining TOML key
collision when one definition needs both a schema property and a target child
with the same name.
The self-schema validates property value shapes, selector mutual exclusivity —
that no more than one selector is declared, though not that at least one selector
is present — conditional completeness, tuple-versus-homogeneous array structure,
nested child applicability, string formats, sibling-rule structures, annotations,
and the selective children namespace.
Rules that require resolving the schema's reference graph cannot be expressed that way and remain schema-load semantics:
- reference existence and cycles;
- effective
allofcompatibility; - conditional branch kinds;
- the presence of the
if.keydiscriminator in a branch's determinate fixed-child set; - collection
itemtypeinherited through composition; - defaults against effective types;
- duplicate
oneof,anyof, andallofentries after type-reference resolution; - allowed values against resolved constraints; and
- sibling-rule operands against the determinate fixed-child set.
A second group of rules needs no reference graph but is still beyond what a schema document can assert about a value, and so also remains schema-load semantics:
versionrejecting a major-version-zero value, which the Semantic Versioningpatternin the self-schema necessarily accepts;- rejecting a definition that declares no selector, no nested child definition,
and no
allof, which the self-schema accepts today because it cannot require that at least one of a selector, a nested child, or anallofbe present; minbeing less than or equal tomax,minlengthbeing less than or equal tomaxlength, and a boundary's TOML kind agreeing with the type it constrains; andpatternandkeypatternvalues compiling at schema-load time and staying inside the portable regular-expression profile, as Pattern requires.
Schema loading also depends on the source-form information required under
Validation and Data Model. That information is what
lets a loader distinguish the inline-table properties default,
dependentrequired, and if from direct child tables with those names, and
validate the latter recursively.
A conforming implementation MUST apply both the self-schema validation and these reference-aware and source-aware schema-load checks.
Precedence
Where this specification and the companion self-schema disagree, this specification governs. An implementation MUST NOT accept a schema this specification rejects merely because the self-schema accepts it, and MUST NOT reject a schema this specification permits merely because the self-schema rejects it. Any such disagreement is a defect in the self-schema and MUST be corrected there rather than in the prose.
This rule is not hypothetical. The two groups listed above are exactly the cases
where the self-schema is knowingly more permissive than the prose, because a
schema document cannot assert those rules about itself. A definition that
declares no selector, no nested child definition, and no allof is the clearest
example: the self-schema accepts it and this specification rejects it, and the
schema is invalid.
Each tagged specification release publishes SPEC.md, toml-schema.abnf, and
toml-schema.tosd together as one versioned bundle, accompanied by a
CHECKSUMS.txt listing a SHA-256 digest for each of those three documents. A
consumer that needs to pin an exact revision of the self-schema SHOULD cite the
release version together with that digest, which identifies one immutable
document rather than a mutable branch path.
Security Considerations
TOML documents and TOML Schema documents are both potentially untrusted inputs, but they present different threats. The common case is a trusted, locally selected schema validating an untrusted TOML document. In that case, implementations MUST bound parsing, recursive structural validation, collection-key matching, alternative evaluation, and diagnostics. An untrusted schema additionally controls the reference graph, regular expressions, annotations, defaults, and the work performed by schema loading. Implementations accepting an untrusted schema MUST apply the self-schema and all reference-aware and source-aware schema-load checks required under Schema Self-Validation before validation, and MUST apply resource limits during those checks as well as during validation.
Schema values are data. An implementation MUST NOT execute or interpolate
description, default, [toml-schema.meta], pattern text, type names, or any
other schema content as program code, shell input, a template, or a filesystem or
network operation. Loading an untrusted schema MUST NOT grant access to files,
environment variables, processes, plugins, or network resources beyond the access
separately authorized to retrieve the schema itself. Version 1.0 type references
are local references into the loaded schema's [types] table, as
Reference Resolution requires; resolving them MUST NOT
perform additional retrieval, and nothing in this section may be read as defining
a cross-file schema import.
Schema Discovery and Retrieval
A [toml-schema].location is controlled by the TOML document being validated, as
described under
TOML Reference of a TOML Schema. Merely
parsing a document or observing that property MUST NOT cause retrieval.
Implementations SHOULD disable document-directed schema retrieval by default and
require the caller to opt in. Callers SHOULD be able to supply a trusted schema
directly instead of enabling discovery. Enabling discovery MUST NOT disable the
checks in this section.
An implementation that retrieves schemas MUST use an explicit URI-scheme
allowlist. The default allowlist SHOULD contain only file for explicitly
authorized local discovery and https for explicitly authorized remote
discovery. Other schemes, including http, MUST NOT be enabled without a caller
decision that names the scheme and accepts its security properties. A URI with a
scheme that is not allowlisted MUST produce a schema-retrieval-refused error. A
relative reference inherits the scheme and authority of its base after resolution
and is subject to the same policy. HTTPS retrieval MUST perform normal certificate
and host-name verification; certificate errors MUST NOT be ignored.
Remote discovery MUST enforce a target policy before connecting. Unless the caller explicitly authorizes a specific internal target, implementations MUST reject loopback, link-local, private-use, multicast, unspecified, and other non-public network destinations. This check MUST cover literal addresses, every address returned by name resolution, and the address of the connected peer, and MUST be repeated for each redirect. Implementations SHOULD prevent DNS rebinding by binding authorization to the addresses checked for the connection. These requirements prevent an untrusted document from using schema discovery as server-side request forgery to reach local services or cloud metadata endpoints.
Retrieval MUST NOT attach cookies, HTTP authentication, client certificates, proxy credentials, ambient cloud identity, or other caller authority by default. A caller MAY explicitly provide credentials for a specific origin. Credentials authorized for one origin MUST NOT be forwarded to another origin, including across a redirect, without separate explicit authorization. Implementations SHOULD reject URI user-information unless the caller has explicitly permitted that credential source, and MUST avoid including credentials in diagnostics.
Redirect following MUST be configurable and bounded. An implementation MUST either
reject redirects or enforce a finite redirect limit; the default limit SHOULD be
no greater than five. Before following each redirect, the implementation MUST
resolve the target and reapply the scheme, network-target, credential, and
resource policies in this section. A redirect to a disallowed target MUST produce
schema-retrieval-refused; exhausting the redirect limit MUST produce
schema-retrieval-failed. A redirect is a retrieval response and does not rewrite
the declared location.
Local schema discovery MUST be confined to a caller-authorized base directory. For
a relative location, resolution still begins at the referencing TOML document's
parent directory, but access MUST be refused unless the resulting schema is within
the authorized base. An absolute file URI is likewise subject to that base.
Implementations MUST reject encoded or decoded parent-traversal segments and MUST
normalize the path, resolve symbolic links or equivalent filesystem indirections,
and verify containment before opening the file. They SHOULD use filesystem
operations that prevent a link or path component from being replaced between
authorization and opening. A path that cannot be proven to remain within the base
MUST produce schema-retrieval-refused.
Resource Limits
Implementations MUST enforce finite, configurable limits appropriate to their environment. At minimum, limits MUST cover:
- the encoded and decoded size of a retrieved schema, including decompressed response data;
- the size, parse depth, and parsed-node count of the target TOML document;
- retrieval duration, including name resolution, connection establishment, redirects, and response reading;
- schema parse depth and size, the number of definitions and references, reference-resolution work, and regular-expression length and compilation resources;
- recursion depth and total work while resolving the reference graph and applying
the required
toml-schema.tosdself-schema; - recursion depth and total work while validating nested tables, collections,
arrays, structural recursion,
allof,oneof,anyof, and conditional branches; and - the number and aggregate size of validation diagnostics retained or emitted.
Limits MUST be active before work begins and MUST apply cumulatively to a single schema-load or validation operation; dividing input among nested definitions or speculatively evaluated alternatives MUST NOT reset them. Implementations SHOULD expose limits to callers and SHOULD choose conservative defaults for network-facing and editor integrations. They MAY stop evaluating after the diagnostic limit is reached, but MUST indicate that diagnostics were truncated and that validation did not complete.
Reaching any limit MUST terminate the affected operation with a
resource-limit-exceeded error. A schema whose loading or self-validation exceeds
a limit MUST remain unloaded. A document whose validation exceeds a limit MUST be
reported as not successfully validated, even if no constraint violation had been
found before the limit was reached.
Regular-Expression Safety
Pattern defines the portable regular-expression profile for
pattern and keypattern, requires every expression to be compiled at
schema-load time, and confines out-of-profile constructs to an explicitly enabled
extension profile. That profile is based on RE2 syntax so that matching can be
implemented without backtracking and with time linear in the parsed string or key
length. This section adds the engine and failure requirements that make that
property real.
Restricting syntax alone is insufficient if the host engine applies a backtracking algorithm to otherwise portable expressions. Implementations MUST evaluate portable patterns with an RE2-equivalent non-backtracking engine or mode that provides the same worst-case linear-time matching property. An implementation whose host engine cannot provide that property MUST use a separate safe engine; it MUST NOT pass untrusted patterns and values directly to a potentially exponential backtracking matcher.
An expression that cannot be compiled MUST fail schema loading with
invalid-pattern, and an out-of-profile expression accepted by no enabled
extension profile MUST fail schema loading with unsupported-pattern. An enabled
extension profile MUST still provide a documented worst-case execution bound and
MUST remain subject to pattern-length, compilation-time, memory, and match-time
limits. Pattern compilation or profile errors MUST NOT be deferred until a
document value or dynamic key happens to exercise the pattern.
Safe Failure
Security policy is part of the validation outcome, not an advisory. Any refusal or incomplete operation required by this section MUST be surfaced as an explicit error through the diagnostic model defined under Diagnostics. An implementation MUST NOT substitute an empty schema, skip the refused check, suppress a limit error, fall back from a rejected URI to a local path, or report successful validation when schema discovery, schema loading, self-validation, reference resolution, or document validation did not complete.
Filename Extension
TOML Schema files MUST use the extension .tosd.
MIME Type
TOML Schema documents are valid TOML documents. When transferring them over the internet, implementations SHOULD use the registered TOML media type:
application/toml
TOML Reference of a TOML Schema
A TOML file can include this indication to reference which schema file to use for validation:
[toml-schema]
location = "<uri>"
version = "1.0.0" # optional
location identifies the schema document. It is REQUIRED when a validator is asked to discover a schema from the TOML document. Its value MUST be a non-empty string containing either an absolute URI, such as an HTTPS URL, or a relative URI reference, such as a local schema filename.
An absolute location MUST be used unchanged. That requirement governs URI
resolution and identity only: the declared absolute URI is the schema's identity
and MUST NOT be rewritten, re-based, or reinterpreted before use. It is not an
authorization to retrieve. Whether the resolved URI may be retrieved at all is
decided by the retrieval policy required under
Schema Discovery and Retrieval, which MAY
refuse it. A relative location MUST be resolved against the referencing TOML document's location, not against the validator's current working directory or the resolved schema's location. For a TOML document stored in a local file, this means resolving a relative location from the document's parent directory.
A validator that receives a TOML document without a base location, for example through standard input, cannot resolve a relative location. It MUST either obtain an explicit base URI from the caller or report that schema discovery failed. An absolute location does not require a document base. Implementations MAY limit the URI schemes they can retrieve, but MUST report an unsupported scheme rather than reinterpret its value as a relative local path.
An implementation that supports the file scheme MUST require a hierarchical
file URI that can be converted to a local path, such as
file:///schemas/config.tosd. It MUST reject an opaque URI such as
file:schema.tosd rather than reinterpret it as a path relative to the TOML
document. A file URI with a query or fragment MUST also be rejected because
those components are not part of the local filesystem path.
version is OPTIONAL. When present, it denotes the expected TOML Schema
language version in the resolved schema document's
[toml-schema].version; it is not an application version or an author-defined
revision of that schema. Its value MUST be a string containing a complete
Semantic Versioning 2.0.0 value: the MAJOR.MINOR.PATCH core is required, and
valid pre-release and build suffixes are optional, as defined by
Schema Versioning.
After resolving and loading the schema, a validator MUST compare these two language versions when the referencing document provides version. A different major version is incompatible and schema discovery MUST fail with unsupported-version. Any other unequal version, including a minor, patch, pre-release, or build metadata difference, MUST produce a version-mismatch warning but MUST NOT by itself cause validation to fail. Compatibility between the resolved schema and the validator remains governed by Schema Versioning.
In a target TOML document, the root [toml-schema] table is reserved for
schema-reference metadata. This is a different context from the metadata table
inside a schema document, which may contain [toml-schema.meta] as described
under Metadata Table - [toml-schema].
location and version are the only keys interpreted by this specification.
Implementations MAY permit additional extension keys, but every direct value in
this table MUST be a TOML scalar: string, integer, float, boolean, offset
date-time, local date-time, local date, or local time. Arrays, inline tables,
subtables, and arrays of tables are not schema-reference metadata and MUST be
rejected during schema discovery with discovery-invalid-metadata.
Implementations MUST ignore extension keys they do not recognize.
When [elements.toml-schema] is omitted, validators MUST ignore the reserved
metadata table during application-data validation. When
[elements.toml-schema] is present, validators MUST additionally validate the
metadata table like any other application table. Schema discovery rules,
including the scalar-only restriction, still apply when discovery is requested.