21. Errors

Format: filename:line:col: error: <message> (LSP-compatible), compile-time, with a span. --strict promotes warnings to errors; --no-warn silences them (SPEC 20).

Every diagnostic carries a stable code — a phase letter (Lex · Parse · Resolve · Validate · laYout · rouTe · Output) then a 3-digit number, e.g. V001. Codes are stable once assigned; the message may still improve. The implementation’s diagnostic registry is the authority for code assignment — this section’s tables and their ordering carry no codes. The human form above stays code-free; lini --json (SPEC 20) emits the structured record — code, severity, span, related span, and a machine-applicable replacement where one exists.

What the --json document freezes. { "file", "diagnostics": [ … ] }, each entry code · family · severity (error / warning) · message · span, then related and suggestion (span · replacement · applicability) where the diagnostic has them; every span carries start · end (byte offsets) and 1-based line · col · endLine · endCol. A tool may rely on that shape and those codes; a message may be reworded to read better, and a later release may add fields — never rename or drop one. A clean file emits an empty diagnostics array, not an error.

Lexing

ConditionMessage
Unclosed stringunterminated string literal
Bad numberinvalid number literal
Bad escapeinvalid escape sequence '\X'
Stray characterunexpected character 'X'
Operator outside a groupmath operators appear inside ( ) — e.g. padding: (8 * 2)

Properties & validation (SPEC 17’s strict/lenient rule)

ConditionMessage
Unknown property nameunknown property 'colr'; did you mean 'color'?
Misused property, wearer known'points' has no meaning on '|box|' — it reads on '|line|' / '|poly|' · 'cell' places a grid or schematic child — this box sits in a 'layout: flow'
Property dead for every wearer'.hot { cell: … }' is inert on every wearer (warning)
Class defined, never wornclass '.hot' is never worn (warning)
Malformed value'opacity' is a fraction 0..1 · 'translate' takes 'x y' · 'padding' takes one value, not a comma list · 'wavy' waves a link's wire — a shape's outline takes solid, dashed, dotted, center, or phantom
A space-separated list where the comma law reads items'data' takes comma-separated values — 'data: 9, 15, 24' (SPEC 2)
Deferred property'legend' is named but not built yet — see SPEC 24 — a named-but-unbuilt row (SPEC 24) errors, so accepting it can never freeze the non-behaviour
Property on a link that has no link meaning'routing' is a scope's strategy — one scope, one strategy; set it on the container (clearance: is a link’s, ROUTING.md)
radius on a non-rect primitive'radius' rounds a rect or a polyline join — rounding a '|hex|' is deferred (SPEC 24)
Arbitrary numeric font-weight'font-weight' takes normal, medium, semibold, bold, or 400, 500, 600, 700 (100–900 is deferred)
Gradient in a text-colour slot'color' takes a flat colour — a gradient fills a shape, and gradient-on-text is deferred
% outside a colour component'width' takes a number — a '%' is a colour component (SPEC 2)

Identity, cascade & statements

ConditionMessage
Duplicate idduplicate id 'X' (previously at L:C)
Unknown type / classunknown type 'X' / unknown class '.X'
Inheritance cycle / depthcycle in 'X → … → X' / 'X' exceeds max inheritance depth (16)
Define shadows builtin'X' shadows a built-in type
Empty bars'| |' needs a type or an '#id'
Invalid id'#123' is not a valid id — an id starts with a letter or '_'
Reserved id prefixan id may not begin 'lini-' — the prefix is reserved for generated names
Class inside the barsa class follows the bars — write '|box| .hot', not '|box.hot|'
Symbol set twicean icon's symbol is its label or 'symbol:', not both
Text carries childrentext content takes no '[ ]' — wrap it in '|block|' to give it children
Box property on text'pin' needs a box — wrap the text in '|block|'
Declaration outside a blocka declaration belongs in a '{ }' block
Bare node on the canvasa node leads with bars — write '|box#X|' (a bare name is a link endpoint)
Bare type in the stylesheeta type only appears in bars — write '|box| { }' to style every box
Missing declaration ‘;’a declaration ends with ';'
Style block holds non-decla '{ }' style block holds only declarations
[ ] holds a declarationdeclarations go in '{ }', not '[ ]'
Two head labelsone inline label — put two or more in a '[ ]'
Label after a classa label comes before classes — write '|box| "X" .hot'
Stylesheet after canvasthe stylesheet '{ }' must come first, before any instance
Glued compound in a rulea selector unit can't glue a type and a class — space them (descendant) or style '.hot'
Spaced class chainclasses glue into a chain — write '.hot.loud', no space

Links & routing

ConditionMessage
Unknown endpoint (path)link endpoint 'X' not found at <scope> + ; did you mean 'A', 'B'?
Auto-create shadows a nodeendpoint 'X' auto-created at <scope> — a node 'X' also exists at 'A.B.X' (warning)
Auto-create near-miss'cta' auto-creates a new box; did you mean 'cat'? (warning — edit distance ≤ 2 or case-fold vs names known in scope, SPEC 3)
Chain mixes operatorslink chain mixes operators 'X' and 'Y'
Chain < 2 nodeslink requires at least two endpoints
Bare o marker'-o' needs a max glyph — write '-o<' or '-o+'; the hollow ring is an ER component only
Missing required property'|line|' requires 'points'
-> in the stylesheet'->' draws a link on the canvas — style every link with '|-| { stroke: … }' in a '{ }' block
|-| / |link| as an instancea link is drawn by an operator — '|-|' only styles links (write 'a -> b') / links are drawn by operators, not the '|link|' type
|node| as instance'node' is the umbrella concept — write '|block|' for the bare box
Unknown routing strategyrouting takes orthogonal, natural, or straight
Unknown side':X' is not a side — use top, bottom, left, or right
Link labels splitkeep a link's labels together — write 'a -> b [ "x" "y" ]' (warning)
Capsule endpoint in a drawinga drawing never invents an endpoint — declare the node, then annotate it
Pin path on an inline component'|component#U9|.p4' — an inline component has no authored pins

Values, colour & expressions

ConditionMessage
Invalid / out-of-range colorinvalid color 'XYZ' / rgb(300,0,0): component out of range
Invalid oklch()oklch expects (L, C, H) or (L, C, H, A) — L and A in 0..1, C ≥ 0, H in degrees
Gradient with < 2 stopsgradient() needs at least two colour stops
linear-gradient without an anglelinear-gradient needs an angle first, then ≥ 2 colour stops — e.g. linear-gradient(135, --teal, --sky)
Single-quoted stringsingle quotes are not strings — use "…"
Unquoted text value'title' takes a quoted string — write title: "…"
Invalid pin value'pin' expects none, center, an edge (top/bottom/left/right), or a corner (e.g. 'top right')
Negative container gapa container's 'gap' must be ≥ 0 — a mate’s gap: may go negative (SPEC 15.5)
skew out of rangeskew: N must be in (-89, 89)
Unknown name in an expressionunknown name 'foo' in an expression
Function arity'sin' takes 1 argument(s), got 2
Spaced call parena call's '(' glues to its name — write 'rgb(…)'
hatch() off fill'hatch' is a fill — 'stroke' takes a colour or gradient
Unreadable image pathcannot read image './logo.svg' — no such file (SPEC 7)
Asset escapes the served root'../secret.svg' resolves outside the served root (SPEC 20)

Layout — grid

ConditionMessage
Missing columns'layout: grid' requires 'columns'
Empty / bad track'columns' needs at least one track / a track is a size, 'auto', or repeat(N[, size])
Grid out of rangecell: 5 _ exceeds columns=3

Layout — wrap (SPEC 5)

ConditionMessage
nowrap text can’t fittext cannot fit 'max-width: 80' without wrapping — widen it or drop 'text-wrap: nowrap'
Non-text child wider than the capa child is wider than 'max-width: 80' — only text wraps
width above max-width'width: 200' exceeds 'max-width: 120'

Layout — tree (SPEC 12)

ConditionMessage
|topic| outside a tree'|topic|' builds a tree — it belongs in a 'layout: tree'
No root topica tree needs exactly one root '|topic|'
A second root topica tree has one root — '|topic|' 'X' is a second
side: top/bottom in bilaterala bilateral tree grows left and right — 'side' takes left or right
side: in row / column'side' picks a bilateral branch's half — this tree has one growth direction
Unknown direction:unknown direction 'radial' — a tree grows column, row, or bilateral (a ring-radial tree is deferred, SPEC 24)

Layout — sequence

ConditionMessage
Sequence node outside a sequence'|loop|' belongs in a 'layout: sequence' (same for |opt| / |alt|; a |note| is core — SPEC 8)
|else| outside an |alt|'|else|' separates an '|alt|' — write it inside one
|note| in a sequence, no placementa sequence '|note|' needs 'place:'
Bad place:'place' is a mode then its lifelines — 'place: over api db', 'place: left api'
Sequence property off a sequence'place' is valid only in a 'layout: sequence' (same for activation)

Layout — chart & pie

ConditionMessage
Series / axis / band / mark outside a chart'|bars|' is a chart series — it belongs in a 'layout: chart' · '|axis|' belongs in a 'layout: chart'
|slice| outside a pie'|slice|' belongs in a 'layout: pie'
Pie given an axis or seriesa pie's children are '|slice|' only
A |slice| with childrena '|slice|' is one wedge — multi-ring pie / sunburst is deferred (SPEC 24)
Empty chart / piea chart needs at least one series / a pie needs at least one '|slice|'
Series with both / neither data: fn:a series takes 'data' or 'fn', not both / a series needs 'data' or 'fn'
arrow / crow marker on a series'marker: arrow' has no centred form on a chart — use dot, circle, or diamond
fn: list ≠ band count'fn' has N formulas but the chart has M bands
Data ≠ categories countseries data has N values but the chart has M categories
labels: count ≠ data count / on fn:'labels' has N entries but the series has M data points / 'labels' needs explicit 'data'
categories: + an axis textset 'categories' or an axis 'labels', not both (⌛ — reachable once per-axis tick text lands, SPEC 24)
|mark| without axis: / bad at:a '|mark|' needs 'axis:' to place it / 'at' takes one value (a line) or two (a point)
|bubble| missing at: / value:a '|bubble|' needs 'at:' (x y) and 'value:'
Unknown axis: idaxis 'X' not found + ; did you mean 'Y'?
range: bad / equal ends'range' takes two ends: 'a b', 'a auto', or 'auto b' / 'range' needs distinct ends
span: on a |band|a band's extent is 'range: a b' — 'span' places a grid child (SPEC 14.5)
scale: log over a non-positive domaina 'scale: log' axis needs a domain above 0
Paint list count ≠ data count'fill' lists N paints but the series has M data points
Paint list on |line| / |area|a '|line|' is one shape with one paint — per-datum lists read on '|bars|' / '|dots|'
Mixed date / numeric domainan axis reads dates or numbers, never both — one domain, one kind
Invalid date literal'2026-13-01' is not a date — ISO-8601: '2026-01-31', optionally 'T09:30' and 'Z'
Numeric step: on a time axisa time axis steps by calendar — 'step: month', 'step: 2 week'
Bad format: value'format' takes auto, decimal N, significant N, scientific N, engineering N, percent N, fraction D, or a date preset
side: in direction: radial'side' has no meaning in a radial chart — it has one radius axis
|band| / |mark| in direction: radiala radial chart draws no bands / marks yet — remove it or change 'direction' (SPEC 24)
hole: out of range'hole' is a fraction 0..1
Negative slice value / pie total zeroa '|slice|' value must be ≥ 0 / a pie's slice values sum to zero

Layout — drawing (SPEC 15)

ConditionMessage
|sketch| without draw:'|sketch|' requires 'draw'
|hole| / |pitch-circle| / |magnifier| without width:'|hole|' requires 'width' — its diameter
Unknown pen call / arityunknown draw call 'X' / 'arc' takes (dx, dy, r) or (r, deg)
fillet / chamfer off a corner'fillet' modifies the corner between two segments
Floating :segmenta ':segment' glues to its call — name a station with point():v
Bare point()'point()' names the pen's position — attach a ':segment'
Arc radius too smallarc radius N is smaller than half the chord
Bad mirror: item'mirror' takes x-axis, y-axis, a bearing, or none
Axis mirror: on |path| / |image|'|path|' has no reflection — draw it with the pen
Bad break: group'break' takes two stations 'a b' — a < b — and an optional x-axis / y-axis
break: off a sketch'break' cuts a '|sketch|' — draw the profile with the pen
break: station off the profile'break' at N misses the profile
Overlapping break: groups'break' spans overlap — merge them
break: through a cubica 'break' can't cut a 'curve()' — move the stations (SPEC 24)
Drawing statement outside a drawing'(-)' draws a dimension — it belongs in a 'layout: drawing' (or its 'floorplan' dialect) (same for (o), (<), ||, corner anchors, tol:, …)
Unknown endpointdimension endpoint 'X' not found at <scope> + suggestions — never auto-created
Corner order':right-top' is not an anchor — did you mean ':top-right'?
(>)'(>)' is reserved — the angle op is '(<)'
One-ended (-) / ||a linear dimension measures two anchors / a mate seats two parts
Two-ended (o)'(o)' measures one round feature — write 'a:top (o)' for a span
Empty one-ended leadera leader needs its text — 'bolt <- "THRU"'
One-ended -> / -*a leader points back at its feature — write 'a <- "…"'
Bare (o) with no axis'(o)' can't pick an axis on 'X' — anchor a side ('X:top (o)') or a segment
(<) on a point anchoran angle reads two edges — a named segment, a '|line|', or a side
Unary (<) on an unmirrored name'(<)' on ':taper' needs 'mirror:' — no twin to measure against
Station on a mirror-only profilea station '⌀' reads a revolved profile — 'revolve: x-axis'
revolve: + mirror: togethera sketch takes 'revolve:' or 'mirror:', not both
Bad revolve: value'revolve' takes x-axis or y-axis
Bad thread: group'thread' takes a segment and its pitch — 'thread: m8 1.5'
thread: without revolve:'thread' dresses a revolved profile — add 'revolve: x-axis'
thread: segment off-axis / not straight'thread' runs along the axis — 'm8' must be a straight run parallel to it
Unknown thread: segmentno segment 'm8' in this 'draw:' + suggestions
thread: on a non-round node'thread' dresses a '|sketch|' segment or a round feature
Bad sheet:'sheet' takes a size — a5…a0 (ISO) or a…e (ANSI) — and an optional portrait / landscape + did-you-mean
of: finds no marker'of' finds no marker 'X'
of: names a non-marker'of' names 'X', not a '|plane|' or '|magnifier|'
Detail of a sourced viewa detail magnifies a base view — 'of' can't name a marker inside another sourced view
at: off the modela 'plane' at N sits off the model
Bad facing:'facing' turns the arrows — left, right, up, or down
Marked projection opa projection line is unmarked — write 'side.screw:head - end.od:top'
Projection ends in one viewa projection link ties two views — both ends read 'side'
Projection end off a viewa projection link ties drawing anchors — 'notes' is not in a drawing view
Cross-view dimension / matea dimension reads one view — a cross-view correspondence is a construction link ('a - b')
Authored cell on a generated fieldcell 2 1 is taken by the generated 'Rev' field — place it after the fields
:segment shadows a built-in point':left' is a built-in anchor — pick another name
Unknown :segmentno segment ':step' on 'body' + suggestions
Duplicate :segment in one draw:':step' is already named in this 'draw:'
Label on a matea mate takes no label
gap: on a point matea point mate coincides — 'gap' needs directed anchors (sides or named edges)
Non-parallel mate directionsmated anchors must face along one axis — 'a:left || b:top' has no shared normal
Over-constrained matemate over-constrains 'X' — already positioned via 'A || B'
Mate within one part'a' and 'b' are features of one part — a part is rigid
Perpendicular directed pair'a:left (-) b:top' — perpendicular faces have no shared normal; the angle between edges is '(<)'
project: vs a directed anchor'project: vertical' conflicts with 'a:left' — the directed anchor reads horizontal
Unknown copy indexno copy 'bolt.5' — the replication places 4
Duplicate datum letterdatum 'A' is already placed (previously at L:C)
Drafting type outside a drawing'|feature-control|' annotates a drawing — it belongs in a 'layout: drawing' (or its 'floorplan' dialect) (same for |surface-finish|, |control|, |datum|)
Unknown characteristicunknown characteristic 'flatnes'; did you mean 'flatness'?
Characteristic set twicea control's characteristic is its label or 'characteristic:', not both
Unknown finish variant'symbol' picks the vee — basic, machined, or prohibited
Control row without tol:a control row needs 'tol' — its zone width
Mixed frame formsa frame is one row or '|control|' rows — not both
|control| outside a frame'|control|' is a '|feature-control|' row
datums: on a form control'flatness' is a form control — it takes no datum
Missing required datum'circular-runout' measures against a datum — name one in 'datums:'
Unknown datum referenceno datum 'D' in this drawing — declared: A, B
Too many datums'datums' orders primary, secondary, tertiary — three at most
zone: off an axial control'zone: diameter' has no meaning on 'flatness' — its zone is a width, not an axis
material: off a feature-of-size control'material' modifies a feature-of-size control — position, orientation, or straightness
Unknown modifier'modifiers' takes projected N, free-state, or tangent-plane
Point-target seata seat needs a face — anchor a side or a named edge ('sf || plate:top')
Seat with no geometry enda seat stands an annotation on geometry — 'sf || n1' seats nothing
Annotation seated twice'sf' is already seated (previously at L:C)
Annotation node on a routed linka routed link's '[ ]' holds text labels — annotation nodes ride a drawing's dimensions and leaders
& fan on a measuring op / mate'&' fans one-ended leaders — chain dimensions instead ('a (-) b (-) c')
gap: on a dimensiona dimension stands off by 'clearance' — 'gap' is a mate's separation
side: off-axisa horizontal dimension stacks on top or bottom / a vertical dimension stacks on left or right
Parallel (<) edgesthe angle's edges are parallel — they never meet
Bad tol:'tol' takes a number, '+upper -lower', or a fit ident
Bad pattern:'pattern' takes grid(cols, rows, dx, dy) or radial(count, radius) (name and arity) · 'radial' needs count ≥ 2 and radius > 0
scale: ≤ 0'scale' must be > 0
scale: on a |page|a '|page|' carries no 'scale:' — 'density:' sets its pixels per millimetre (root), a drawing's 'scale:' its drafting ratio
density: ≤ 0'density' must be > 0
density: under unit: px'density' is pixels per millimetre — 'unit: px' has none; drop it, or state 'unit: mm' to scale (warning — only the identity density: 1 applies, SPEC 15.1)
Absurd rendered extentthe drawing renders 48000 px wide — 'scale:' is a ratio; a 5 m beam at 1:50 is 'scale: 0.02' (hint)
Bad unit: / density: off the root'unit' is px, mm, cm, m, or in / 'density' is scene config — set it in the root block
Chain past a labela text callout ends its statement — chain before it
Mate in a flow scopea '|row|' places its own children — mates seat a drawing's
Empty drawinga drawing needs at least one geometry child

Layout — floorplan (SPEC 15.11)

ConditionMessage
Floorplan type outside the scope'|wall|' belongs in a 'layout: floorplan' (every floorplan type)
on: an unknown / curved segment'sout' is not a segment of this wall; did you mean 'south'? · an opening sits on a straight run — ':bay' is an arc
An opening off its segment'd2' at 1.8 + width 0.9 overruns 'side' (length 2.3)
Overlapping openings'entry' and 'w1' overlap on 'south'
An opening outside a wall’s [ ]a '|door|' rides in its wall's '[ ]'
translate: on an openingan opening sits at 'on:' / 'at:' — move the station, or nudge the wall
curve() in a wall’s draw:a wall bends with 'arc()' — 'curve()' has no offset
hinge: / swing: on a sliding doora sliding door has no leaf to hang — remove 'hinge:' / 'swing:'
A wall segment authored *-in / *-out':north-in' collides with the derived face anchor — rename the segment
An arc wall tighter than its thicknessarc radius 40 is under thickness/2 — the inner face vanishes
Missing on: / steps:required-property errors, as points on a |line|

Layout — schematic (SPEC 16)

ConditionMessage
Schematic type outside the scope'|R|' belongs in a 'layout: schematic' (every schematic type)
:side on a terminala terminal owns its connection — a pin or label takes no ':side'
Non-90° rotation on a connection-bearing parta schematic part rotates in 90° steps — 0, 90, 180, or 270
A mirror: on such a part naming no axis of its owna schematic part mirrors about its own axis — x-axis, y-axis, or none
Pinless wire to a 3+-pin part'U7' has 21 pins — name one ('U7.VS')
Both pins of a 2-pin part takenboth pins of 'R5' are wired — name one ('R5.p1')
Minted ref as an endpointlink endpoint 'R1' not found — a minted ref is display-only; give the part an id to wire it
Marker on a part-to-part wirea schematic wire is plain — markers shape a text label's tag; write 'a - b'
Marker at a symbol-form label'|gnd|' draws its symbol — there is no tag to shape
Bare unknown id in the scope'NSTDBY' is unknown — a schematic never invents a box; did you mean '- "NSTDBY"' (a net label)?
Duplicate wire'a - b' is already wired — a repeated wire means nothing on a sheet
Dot-path into a labela label is its own terminal — it has no pins
Unknown schematic symbol:unknown symbol 'gnb'; did you mean 'gnd'?
Bad shape: / pins: / number:'shape' takes plain, left, right, both, or round — not 'X' · 'pins' takes a count ≥ 1 · 'number' takes an integer
clearance past the fine pitch'clearance' 30 is past the sheet's pin pitch 20 — two wired pins stand one pitch apart, so no wire could keep it
Non-integer columns'columns' in a schematic is the wrap count — a positive integer (its tracks size to their anchors)
Two anchors on one cell:cell 2 1 already holds 'U7' — give 'U8' its own ordinal
Satellite chain with no placed end'C7' has no placed end — its chain falls back to the flow (warning)
A chain’s third placed end'U3.a' also holds 'C7' — a chain distributes between two placed ends, so this one is dropped (warning)

Routing — a stray’s reasons (ROUTING.md, Impossible layouts / Fixed ports); each is a warning naming the link, --strict an error

ConditionMessage
Closed graphno legal route: every side entry or channel is closed at this layout
Blocked fixed portfixed port blocked: a body covers the port's landing
Crowded fixed portsfixed ports closer than the minimum pitch on one side
Conflicted fanfan ends carry two different fixed ports
Pinned self-loopself-loop with both ends forced onto one side

Output (SPEC 18)

ConditionMessage
--static cannot outline a runno bundled glyph for '你', '好' in "你好" — under --static this text stays <text>, drawn by the viewer's fonts (warning)