Skip to main content

SYN — syntax and composition

There is only one SYN code.

CodeSeverityWhat it says
SYN01errorA syntax or composition error

SYN01 is not an individual code. It is a copy, gathered into one, of the exception the parser threw. Since the file never became a model, not one semantic check ran — with even a single syntax error, the result of check is exactly "one SYN01".

It appears only under --json

grid X 0 3600
grid Y 0 4000
level L1 0
space /L1/a room X1..X9 Y1..Y2
koyu check bad.muro --json
[
 {
  "code": "SYN01",
  "severity": "error",
  "message": "Undefined grid line name: X9",
  "line": 4,
  "file": "<absolute path>/bad.muro"
 }
]

A check without --json, and every other subcommand, print the exception as-is and exit 1.

koyu check bad.muro
✖ <absolute path>/bad.muro:line 4: Undefined grid line name: X9

The single SYN01 exists only so that valid JSON can still be returned. A machine running check --json should not have syntax errors come back in a different shape from everything else.

Common bodies and their fixes

Grids and levels

BodyCauseFix
Undefined grid line name: X1grid X has not been written yetWrite grid X / grid Y before any line that uses a grid line. grid and level cannot be forward-referenced (a boundary can)
Undefined grid line name: X9That grid line exceeds the number in the gridWith grid X 0 3600 7200 only X1–X3 exist
grid coordinates are written in ascending orderThe coordinates descendReorder them ascending
grid X is declared once (in the base layer when composing)Several layers carry grid XConsolidate into the base layer (the entry)
Undeclared level: level:L9What level: points at does not existDeclare level L9 …, or fix the spelling
Duplicate level: L2The same level name declared twice (including a clash with a range declaration)Delete one
The level height (z) is not a number:No z was written, as in level L1level L1 0
A level range requires pitch: (the storey height in mm): L1..L3A range declaration with no storey heightlevel L1..L3 0 pitch:3000
level carries spec:, which is not in the ledger (level reads h / slab / pitch / underground)An unknown key on levellevel reads only those four. Put other information on a space or a zone
The range includes an undeclared level (declare level first): L1..L2A stack or column naming an undeclared levelWrite the level first

Spaces, boundaries, zones

BodyCauseFix
A region is given as two ranges, X?..X? and Y?..Y?The type (the second positional) was forgottenspace /L1/a X1..X2 Y1..Y2Add the type: space /L1/a room X1..X2 Y1..Y2. The body talks about the region, but the cause is usually the missing type
space /L1/a requires a type (a word from the vocabulary)Neither type nor regionAdd the type
Duplicate space path: /L1/a (first seen …)Two spaces at the same pathThe path is the identity. Change one of them
boundary takes the form boundary /pathA /pathB [attributes...]The second path is missingA boundary is a relation joining two spaces
zone takes the form zone /path [attributes...]No path
Unknown keyword: wallThat keyword does not existA wall is a relation, not a thing. Use boundary
The attribute h is written as a number: 24OOA numeric attribute on level does not read as a numberWrite a number with no unit

Indentation

Openings, seg and line are written indented under a boundary; area and band members under a space.

BodyCauseFix
Unknown keyword: doorThe door line is not indentedPut whitespace at the head of the line so it is subordinate to its boundary
door is written indented directly under boundaryIt is indented, but the preceding unindented line is not a boundaryMake the parent a boundary (the case above had it under a space)
area is written indented directly under spaceThe indented area's parent is not a space
Only door / window / seg / line / area / space (a band member) may sit on an indented line: noteSome other word on an indented lineOnly those six may be indented
Only + (add) / - (remove) / = (replace) may sit directly under over: doorSomething other than a set edit under overWrite it as - door D1
One boundary carries one line: /L1/a | /L1/bA second line under one boundarySplit it into two boundaries

Openings and attributes

BodyCauseFix
door requires a width w:(mm) (the asset may supply it)The opening has no w:Write door w:800, or reference an asset that carries a width (door SD1)
Duplicate attribute key: nameThe same key twice on one lineConsolidate into one. The later one is never silently taken
Unclosed quoteAn odd number of "Close it
An attribute is written key:value: 居室A token with no : is in an attribute positionMake it key:value. To include whitespace in a value, wrap it in "…"

Versions and composition

BodyCauseFix
Unsupported koyu version: 0.9 (this tool supports 0.1, 0.2, 0.3, 0.4, 0.5, 1.0)A version that does not existUse one of the six
The koyu version is declared only in the base layer (the entry)A version in an imported layerMove it to the base layer
The koyu version is declared once (already 1.0)The version written twiceDelete one
Cannot read file: ./assets.muroThe import's relative path is wrongA path is resolved relative to the file it is written in

How far misspellings are caught

A misspelled attribute key is caught. nmae:居室A is not carried through as a free attribute; it is an ATT03 error. A key not in the ledger cannot be written without a dot-separated namespace (acme.nmae).

A misspelled type (the second positional) is not caught. The type is an open vocabulary, so writing bedroom as bedrom is not an error.

grid X 0 3600
grid Y 0 4000
level L1 0 h:2400 slab:150
space /L1/a bedrom X1..X2 Y1..Y2

That file is green. A type is used for little more than an aggregation axis and the paleness of a drawing; it is not the entrance to any judgement, which is why it stays open. The daylight scope is decided by daylight:1, not by the type (DAY01), so no verdict can vanish because of a misspelled type.

The real work starts once syntax passes

SYN01 disappearing means only that the file became a model. From there the semantic checks — the other 64 codes — run.

koyu check house.muro --strict

--strict makes warnings exit 1 as well. That is the one to put in a CI gate.