Validation Errors [[validation_errors: SyntaxConcept]]SyntaxConcept
Validation errors detected by the workspace validate command.
Broken Link [[err_broken_link: ValidationError]]ValidationErrorDiagnostics McpResource 74%Validate References McpTool 69%Validation Validation 68%
A [[#id]] reference points to a non-existent object in the workspace (after both __id and __local_id lookups fail).
- code: broken_link
- severity: error
Cause [[cause: text]]textWorkspace In Wrong File DiagnosticRule 91%Workspace SyntaxConcept 80%Object SyntaxConcept 80%
- The object with the specified ID does not exist (neither as
__idnor as__local_id) - Typo in the reference ID
- The object was deleted but references remain
- Incorrect namespace in the reference
Note: before producing a broken_link error, the validator attempts __local_id fallback — if the reference matches exactly one object's __local_id, it resolves successfully without error.
Solution [[solution: text]]textWorkspace In Wrong File DiagnosticRule 87%Reference SyntaxConcept 85%Code Action LSPFeature 81%
- Verify the target object exists
- Fix the ID in the reference
- Create the missing object
- Add a qualifier to the reference:
[[#namespace:id]]or[[#workspace:namespace:id]] - Use the full hierarchical ID:
[[#parent.child]]instead of[[#child]]
Duplicate ID [[err_duplicate_id: ValidationError]]ValidationErrorAmbiguous Reference DiagnosticRule 72%
Two objects with the same Kind:Id in one namespace. Every occurrence after the first is reported,
and candidates lists all of them, in the order the files are read: directory, then
readme.qmd.md, then file name, each compared by UTF-8 bytes.
- code: duplicate_id
- severity: error
Cause [[cause: text]]text
- Copying objects without changing the ID
- Auto-generated IDs from identical Titles
- Git merge conflicts
Solution [[solution: text]]text
- Rename one of the objects (change the ID)
- Use explicit
[[id]]instead of auto-generation - Move objects into different namespaces
- Change the Kind of one of the objects
Ambiguous Reference [[err_ambiguous_ref: ValidationError]]ValidationErrorAmbiguous Field Reference DiagnosticRule 90%Invalid Reference Syntax DiagnosticRule 81%Duplicate ID DiagnosticRule 74%
A [[#id]] reference could point to multiple objects (ID collision or multiple __local_id matches).
- code: ambiguous_reference
- severity: error
Cause [[cause: text]]text
- Same ID in different namespaces
- Reference without a namespace qualifier
- Multiple objects share the same
__local_id(e.g., several child objects named[[config]]under different parents, all with__local_id: "config")
Two objects with the same ID in ONE namespace are a duplicate_id, whatever their Kinds.
Solution [[solution: text]]text
- Add namespace to the reference:
[[#storage:users]] - Use the full form:
[[#workspace:storage:users]] - Use the full hierarchical ID:
[[#parent.config]]instead of[[#config]]
Nested Workspace [[err_nested_workspace: ValidationError]]ValidationErrorWorkspace SyntaxConcept 79%Validation Validation 76%Get Tree McpTool 76%
A workspace inside another workspace, left out of the result: its files are not part of the outer workspace, so they are missing from the graph.
- code: nested_workspace
- severity: error
Cause [[cause: text]]text
- A
__Workspaceobject was created inside an existing workspace - Incorrect directory structure
Not reported when the inner workspace is composed alongside the outer one — -w repo -w repo/.qmdc,
or a directory holding both. Its files are then in the result under its own workspace, so nothing
is missing. This is the layout qmdc-wiki gives a repository it models.
Solution [[solution: text]]text
- Compose both:
qmdc workspace validate -w <outer> -w <outer>/<inner> - Move the nested workspace up one level (make them siblings)
- Change the Kind to
__Namespaceinstead of__Workspace, if it was meant to be part of the outer workspace - Delete the nested workspace
Workspace In Wrong File [[err_workspace_in_wrong_file: ValidationError]]ValidationErrorWorkspace In Wrong File DiagnosticRule 89%Validation Validation 83%Workspace SyntaxConcept 82%
A __Workspace object is defined in a file other than readme.qmd.md.
- code: workspace_in_wrong_file
- severity: error
Cause [[cause: text]]text
__Workspace declarations must be in readme.qmd.md (the anchor file for the workspace root). If a __Workspace object appears in any other file, the parser generates this error.
Solution [[solution: text]]text
- Move the
__Workspacedeclaration toreadme.qmd.md - If the file is a namespace, use
__Namespaceinstead of__Workspace
Structured In TextBlock [[err_structured_in_textblock: ValidationError]]ValidationErrorField SyntaxConcept 71%Validate a Document HowTo 69%Syntax Syntax 67%
Attempt to create a structured element (object/field) inside a __TextBlock.
- code: structured_in_textblock
- severity: error
Cause [[cause: text]]text
__TextBlock is a system object for unstructured content. It is created when:
- The document starts with a heading without
[[id]] - A heading without
[[id]]appears at the top level (not inside an object) - After a code fence at the top level
Inside a __TextBlock, structured elements are forbidden:
- Headings with
[[id]]or[[id: Kind]] - Field lists
- key: value
Examples [[examples: text]]textValidate a Document HowTo 83%Common Errors NarrativeDoc 81%Syntax Syntax 80%
Error example:
## Documentation Section
This starts a TextBlock (heading without [[id]]).
### Some Object [[my_obj]]
Error! Attempt to create an object inside a TextBlock.
Important exception — do not confuse TextBlock with an object's comment section:
A comment section is a heading without [[id]] inside an object. It is part of the object (__comments field), not a TextBlock.
# My Object [[my_obj: Kind]]
- name: Example
## Comment Section
This is a Comment inside an object, NOT a TextBlock!
### Nested [[nested_obj: NestedKind]]
This is a valid nested object, NOT an error.
In an object's comment section, a heading with [[id: Kind]] creates a nested object — this is normal behavior.
Solution [[solution: text]]text
- Add
[[id]]to the parent heading to create an object instead of a TextBlock - Move the structured content to the top level
- Remove
[[id]]from the nested heading if it is just text
Multiple Definitions In Heading [[err_multiple_definitions: ValidationError]]ValidationErrorDuplicate ID DiagnosticRule 75%Heading SyntaxConcept 71%Ambiguous Field Reference DiagnosticRule 68%
A heading contains more than one [[...]] definition.
- code: multiple_definitions
- severity: error
Cause [[cause: text]]text
Each heading may contain at most one [[id]] or [[id: Kind]] definition. If a heading has two or more definitions, the parser cannot unambiguously determine which one is the object ID and which is a field. This is a syntax error.
Examples [[examples: text]]text
## Also broken [[items: array]] [[finding_array: Finding]]
Here [[items: array]] is a field definition and [[finding_array: Finding]] is an object definition. The parser does not know which is the heading's ID.
Solution [[solution: text]]text
- Split into two headings:
## Findings [[finding_array: Finding]]
### Items [[items: array]]
- severity: high
- Or use a single definition with fields:
## Also broken [[finding_array: Finding]]
- severity: high
- category: parser
Ordered List In Array [[err_ordered_list_in_array: ValidationError]]ValidationErrorArray SyntaxConcept 68%Common Errors NarrativeDoc 65%
A numbered list (1. item) is used inside a heading-syntax array instead of a bullet list.
- code: ordered_list_in_array
- severity: error
Cause [[cause: text]]text
QMD.md supports only bullet lists (- item) for markdown-list arrays. Numbered lists (1. First, 2. Second) are forbidden — numbering is redundant since arrays are ordered by definition.
The numbered list content is preserved in __comments for lossless round-trip, but the parser generates an error.
Examples [[examples: text]]text
## Task [[task1: Task]]
- category: implementation
### Steps [[steps: array]]
1. First step
2. Second step
3. Third step
Solution [[solution: text]]text
Replace the numbered list with a bullet list:
### Steps [[steps: array]]
- First step
- Second step
- Third step
Or use YAML notation:
- steps: [First step, Second step, Third step]
Table In Array [[err_table_in_array: ValidationError]]ValidationErrorArray SyntaxConcept 73%
A Markdown table is used under a primitive array field ([[field: array]]).
- code: table_in_array
- severity: error
Cause [[cause: text]]text
A primitive array holds scalar values, and a table has columns — there is no defined mapping from one onto the other, so the parser cannot decide what a row should become.
This differs from an OBJECT array ([[field: [Kind]]]), where a table IS valid: each data row
becomes one object and the column names become its fields. The distinction is the declared field
type, not the table.
The table content is preserved in __comments for lossless round-trip, but the parser generates an
error.
Examples [[examples: text]]text
## Doc [[doc]]
### Tags [[tags: array]]
| a |
|---|
| 1 |
Solution [[solution: text]]text
If the values are scalars, use a bullet list:
### Tags [[tags: array]]
- one
- two
If each row is meant to be an object, declare an object array and give it a Kind:
### Tags [[tags: [Tag]]]
| name | colour |
| ---- | ------ |
| one | red |
Extra Table In Array [[err_extra_table_in_array: ValidationError]]ValidationError
A second Markdown table appears under one object-array heading.
- code: extra_table_in_array
- severity: error
Cause [[cause: text]]text
The array heading's own table is what feeds the array — each data row becomes one object, with a positional id derived from the row's index. A second table under the same heading cannot extend the array, because its rows would generate the same ids as the first table's, and the heading declares an array rather than prose, so the table describes nothing.
The table content is preserved in __comments for lossless round-trip, but the parser generates an
error.
Note this applies only to the array CONTAINER's own content. A table inside an array ELEMENT is that element's content and is perfectly valid — see the Arrays section.
Examples [[examples: text]]text
## Team [[team: Group]]
### Members [[members: [User]]]
| name |
| ----- |
| Alice |
| name |
| ---- |
| Bob |
Solution [[solution: text]]text
Put every row in one table:
### Members [[members: [User]]]
| name |
| ----- |
| Alice |
| Bob |
Mixed Array [[err_mixed_array: ValidationError]]ValidationErrorArray SyntaxConcept 71%Invalid Field Type DiagnosticRule 68%
An array section holds something that cannot be one of its elements: an object array fed by a table
AND also carrying heading elements, or a declaration inside a primitive [[field: array]].
- code: mixed_array
- severity: error
Cause [[cause: text]]text
An object array is written in exactly one of two forms — a table, where each data row becomes an object, or subheadings, where each subheading becomes an object. Mixing them is not supported: once the array has been built from the table, a following element heading cannot join it, so it silently becomes a plain field on the parent and its declared Kind is lost.
Note this fires only when the TABLE comes first. A table AFTER an element heading is that element's own content, which is valid — see the Arrays section.
The same code covers a PRIMITIVE array, [[field: array]], that contains a deeper heading declaring
an identifier. A primitive array's elements are list items, and a heading is not one, so the
declaration cannot become an element — and the heading does not become an object either, because the
declared kind decides what the section is — the rule docs/format/headings.qmd.md states for text.
The
heading closes the array's content, exactly as prose between two lists does, and its own text is not
preserved: the document is invalid and has to be fixed.
Examples [[examples: text]]text
## Team [[team: Group]]
### Members [[members: [User]]]
| name |
| ----- |
| Alice |
#### Bob [[bob]]
- role: dev
Here bob does not join members: it becomes team.bob, a field on team, with its Kind degraded
from User to __Object.
A declaration inside a primitive array:
## Session [[s: Session]]
### Closure [[clo: array]]
- one
- two
#### Task [[t5: SessionTask]]
- status: x
clo keeps exactly ["one", "two"], no object s.clo.t5 or s.t5 is created, and one mixed_array
error points at the #### Task line.
Solution [[solution: text]]text
Use one form for the whole array. Either put every element in the table:
### Members [[members: [User]]]
| name | role |
| ----- | ----- |
| Alice | |
| Bob | dev |
Or write every element as a subheading:
### Members [[members: [User]]]
#### Alice [[alice]]
#### Bob [[bob]]
- role: dev
Explicit System Type [[err_explicit_system_type: ValidationError]]ValidationErrorInvalid Field Type DiagnosticRule 75%Unknown Kind DiagnosticRule 72%Validation Validation 69%
Explicit declaration of a system type __Document, __TextBlock, or __Object in a heading.
- code: explicit_system_type
- severity: error
Cause [[cause: text]]text
System types __Document, __TextBlock, and __Object are created by the parser automatically only. Explicit declaration of [[id: __Document]], [[id: __TextBlock]], or [[id: __Object]] in a heading is forbidden.
Types __Workspace and __Namespace allow explicit declaration in anchor files (readme.qmd.md).
Examples [[examples: text]]text
# Test Document [[test_doc: __Document]]
- version: 1.0
Solution [[solution: text]]text
- Remove the system type from the heading:
# Test Document [[test_doc]] - Use a user-defined type:
# Test Document [[test_doc: Document]]
Dangling Field [[err_dangling_field: ValidationError]]ValidationErrorMissing Required Field DiagnosticRule 83%Ambiguous Field Reference DiagnosticRule 80%Invalid Field Type DiagnosticRule 77%
A heading-syntax field is declared without a parent object at a higher heading level.
- code: dangling_field
- severity: error
Cause [[cause: text]]text
Heading-syntax field types (text, array, yaml, json, object_array) imply a parent object at a higher heading level. If no such parent exists (field at the top level or at the same level as a sibling object), this is an error.
The parser creates an object for lossless round-trip but generates a dangling_field error.
Examples [[examples: text]]text
## Result [[result1: Finding]]
- status: done
## Summary [[summary: text]]
This is a text field at the same H2 level as Result — no parent object exists.
Here [[summary: text]] is a heading-syntax field of type text, but it is at the same H2 level as [[result1]]. There is no parent object at a higher level (H1) that could contain this field.
Solution [[solution: text]]text
- Add a parent object at a higher level:
# Document [[doc1]]
## Result [[result1: Finding]]
- status: done
## Summary [[summary: text]]
Summary text here.
- Or remove the field type and make it a regular object:
## Summary [[summary]]
- content: Summary text here.
Invalid Map Entry [[err_invalid_map_entry: ValidationError]]ValidationErrorInvalid Field Type DiagnosticRule 72%Data Type SyntaxConcept 72%
A list item inside [[field: map]] is not a valid key: value pair.
- code: invalid_map_entry
- severity: error
Cause [[cause: text]]text
A map field expects a bullet list with items in the format - key: value, where key is a valid QMD.md key ([a-zA-Z_][a-zA-Z0-9_]*). The error is generated when:
- The key contains Markdown formatting (
**bold**,`code`) - The list item does not contain a colon (
- just text) - The item is a link (
- [link](url))
Invalid items are discarded; valid pairs are kept in the map.
Examples [[examples: text]]text
# Service [[svc1]]
### env [[env: map]]
- host: localhost
- **port**: 8080
- path: /api
The line - **port**: 8080 generates an error — bold formatting makes the key invalid.
Solution [[solution: text]]text
Remove Markdown formatting from keys:
### env [[env: map]]
- host: localhost
- port: 8080
- path: /api
Broken Parent [[err_broken_parent: ValidationError]]ValidationErrorSyntax Syntax 76%Validation Validation 74%Workspace Validate Command 71%
Parent object not found for a dot-ID declaration.
- code: broken_parent
- severity: error
Cause [[cause: text]]text
A dot-ID object [[parent_path.local_id]] declares a hierarchical ID where parent_path does not resolve to an existing object in the workspace. The parent must exist for the child to attach to it.
Examples [[examples: text]]text
## Alice [[team.members.alice]]
- role: admin
If no object with __id equal to team exists (or team has no members field path), the parser cannot establish the parent relationship.
Solution [[solution: text]]text
- Create the parent object first:
## Team [[team]]with amembersarray field - Fix the parent path in the dot-ID: ensure each segment resolves to an existing object
- Use a flat ID instead if hierarchy is not needed:
[[alice]]
Ambiguous Field Reference [[err_ambiguous_field_reference: ValidationError]]ValidationErrorAmbiguous Reference DiagnosticRule 79%Field SyntaxConcept 74%Reference Reference 73%
A dot-path reference cannot be unequivocally resolved to an object or a field.
- code: ambiguous_field_reference
- severity: error
Cause [[cause: text]]text
A dot-path reference resolves both as an object ID and as a field-path on a parent object. The parser cannot determine whether the reference targets the object or the field.
Exemption: the error is NOT raised when the parent's field value is exactly the parser-generated child link [[#<full dotted id>]]. Every nested child produces such a field on its parent (e.g. svc gets config: "[[#svc.config]]"), so without this exemption every hierarchical parent→child reference would be flagged. Only a field whose value is anything else (a scalar, a different reference) makes the dot-path genuinely ambiguous.
Examples [[examples: text]]text
## Team [[team]]
- status: active
## Status [[team.status]]
- value: operational
Here [[#team.status]] is ambiguous — it could reference the field status on object team, or the object with hierarchical ID team.status.
Solution [[solution: text]]text
- Rename the child object to avoid collision with the field name
- Remove the field from the parent if the object is the intended target
- Use a non-hierarchical ID for the object:
[[team_status]]
Invalid Map Content [[err_invalid_map_content: ValidationError]]ValidationErrorData Type SyntaxConcept 68%
Content inside [[field: map]] that is not a bullet list with key: value pairs.
- code: invalid_map_content
- severity: error
Cause [[cause: text]]text
A map field accepts only a single bullet list with - key: value items. Any other content between the map heading and the next heading at the same or higher level is an error: paragraphs, code fences, numbered lists, additional bullet lists, and deeper headings — including a deeper heading that DECLARES an identifier, which creates no object, because the declared kind decides what the section is.
Invalid content is ignored; the map is populated only from the first valid bullet list. An entry after an offending block does not join the map.
A map is a flat str -> str dictionary, so an indented sub-item is not an entry either: under a - key: with no value it is invalid_map_entry's sibling error nested_subitems and the key is dropped; under a - key: value the sub-item is dropped and the entry keeps its own value.
Examples [[examples: text]]text
````markdown example
Service [[svc1]]
env [[env: map]]
Some description paragraph.
- host: localhost
-
port: 8080
-
numbered item
some: code
The paragraph, numbered list, and code fence generate invalid_map_content errors.
Solution [[solution: text]]text
Remove all content except the bullet list with key: value pairs:
```markdown
env [[env: map]]
- host: localhost
- port: 8080 ```
Block In Inline Field [[err_block_in_inline_field: ValidationError]]ValidationErrorSyntax Syntax 70%YAML Block SyntaxConcept 69%Validate a Document HowTo 67%
An indented block follows an inline field that already has a value.
- code: block_in_inline_field
- severity: error
Cause [[cause: text]]text
An inline field (- key: value) holds a scalar. It has no content of its own, so an indented table,
list, paragraph, quote or fence placed under it belongs to nothing — the format defines no meaning
for it.
This is the sibling of nested_subitems, which covers the EMPTY-value form (- key: followed by
indented items). Here the value is present and the block follows it.
Not to be confused with YAML multiline (- key: | or - key: >), where the indented block IS the
field's value. That is valid and is not reported.
The block content is preserved in __comments, anchored on the field and with its indentation
removed, for lossless round-trip; the parser generates an error.
Examples [[examples: text]]text
```markdown
P [[p: G]]
-
lead: Ann
-
note: something
| ic | |----| | x | ```
Solution [[solution: text]]text
If the block belongs to the object, put it outside the field list:
```markdown
P [[p: G]]
- lead: Ann
- note: something
| ic | |----| | x | ```
If it belongs to the field, declare a heading-syntax text field instead:
```markdown
P [[p: G]]
- lead: Ann
Note [[note: text]]
something
| ic | |----| | x | ```
Nested Subitems [[err_nested_subitems: ValidationError]]ValidationErrorValidation Validation 71%Field SyntaxConcept 70%
A nested list under an inline field (- key: followed by indented - item lines).
- code: nested_subitems
- severity: error
Cause [[cause: text]]text
Inline fields are flat — a list item cannot carry its own sub-list. The pattern - key: with indented - item children is rejected.
Solution [[solution: text]]text
Use a YAML array (- items: [first, second]) or heading-syntax (### Items [[items: array]] with a bullet list).
Mixed Field Keys [[err_mixed_field_keys: ValidationError]]ValidationErrorInvalid Field Type DiagnosticRule 77%Ambiguous Field Reference DiagnosticRule 74%Missing Required Field DiagnosticRule 71%
An object's field list mixes valid and invalid keys.
- code: mixed_field_keys
- severity: error
Cause [[cause: text]]text
Valid keys match [a-zA-Z][a-zA-Z0-9_]*. When SOME items in one object's list are valid fields and others are not (spaces, hyphens, leading digits, markdown formatting in the key), the parser cannot decide whether the list is a field list or plain text.
Solution [[solution: text]]text
Fix the invalid keys (First Name: → first_name:), or convert the list to a text field if it is prose.
Unsupported Number Format [[err_unsupported_number_format: ValidationError]]ValidationErrorValidation Validation 69%
A field value looks like a number QMD.md cannot carry.
- code: unsupported_number_format
- severity: error
Cause [[cause: text]]text
QMD.md's numeric grammar is deliberately narrow: -?\d+(\.\d+)?, with a magnitude between 1e-4
and 2^53-1. Two kinds of value trip this error.
A spelling the format does not define — 1e5, 1.5e-3, 2E3, .5, 5., +1, 1_000, 0x1f,
0o17. Each of these only ever worked in whichever host language happened to accept it, which is
exactly why the three parsers used to disagree about them.
A value in a supported spelling but outside the range. Above 2^53-1 an integer cannot survive a round
trip through a double — 9223372036854775807 comes back as a different number in a JavaScript
reader — and below 1e-4 a decimal can only be written with an exponent, which the grammar has no
form for.
The field keeps the text the author wrote, as a String, so nothing is lost and the document still round-trips. The error exists so the spelling is refused out loud instead of silently becoming a string.
```markdown
Config [[config: Settings]]
- retries: 3 ← fine
- rate: 1e-3 ← error: exponent
- max: 9223372036854775807 ← error: beyond 2^53-1
- epsilon: 0.00001 ← error: needs an exponent to write back
- released: 2026-09-24 ← fine, a plain string and no error ```
Solution [[solution: text]]text
Write the value in the supported grammar (0.001 rather than 1e-3), or quote it to say a String
was meant all along (- max: "9223372036854775807").
A quoted value raises no error, which is the intended escape hatch: the quotes state that the text is the value.
The error carries a hint field naming which of those two ways out applies, so the reader is told
what to write rather than only what is wrong. A spelling the grammar does not define gets write a
plain integer or decimal such as 42 or -1.5 (or quote the value to keep it as text); a magnitude
outside the range gets magnitude outside 0.0001..9007199254740991 (quote the value to keep it as
text), since quoting is then the only way out. It is the only error that carries a hint.
Invalid ID Character [[err_invalid_id_character: ValidationError]]ValidationErrorCommon Errors NarrativeDoc 74%Duplicate ID DiagnosticRule 74%Validation Validation 73%
A dot in a NESTED heading's explicit ID.
- code: invalid_id_character
- severity: error
Cause [[cause: text]]text
Dot-ID declarations ([[parent.child]]) are legal only on top-level headings. A nested child's hierarchical ID is composed automatically from its parent; an explicit dot in a nested [[id]] is a parsing error.
```markdown
Parent [[parent]]
Child [[child.invalid]] ← error: dot in nested explicit ID
```
Solution [[solution: text]]text
Use a simple local id for the nested heading ([[child]]); the dot-path (parent.child) is composed automatically.
Wrapped Field Value [[err_wrapped_field_value: ValidationError]]ValidationErrorInvalid Field Type DiagnosticRule 79%Ambiguous Field Reference DiagnosticRule 72%Validation Validation 72%
A field value, or an array element, continued on an indented second line.
- code: wrapped_field_value
- severity: error
Cause [[cause: text]]text
A value is written on one line. A continuation line was read three different ways — Rust joined the lines with a space, Python with a newline, TypeScript kept only the first line and dropped the rest in silence — so the same document carried three different values depending on which parser read it. The value is now the authored first line everywhere, and the continuation is reported instead of being absorbed or discarded quietly.
```markdown
Session [[s: Session]]
- note: first line second line ```
note is first line, and one wrapped_field_value error points at the continuation's line. An
element of a [[field: array]] list behaves the same way.
The two legal multiline forms are untouched, because each announces itself on the first line: YAML
pipe (- key: |) and a YAML array whose bracket opens the value (- key: [).
Solution [[solution: text]]text
Put the value on one line, use the YAML pipe form for genuinely multiline text, or move it to a
heading-syntax text field (### Note [[note: text]]).