<!-- GENERATED by scripts/gen-error-codes.ts from src/error-catalog.ts — do not edit by hand. -->

# ArchLang error codes

Every diagnostic carries a stable code. Look one up with `arch explain <CODE>`
(e.g. `arch explain E_ROOM_SIZE`). Errors abort rendering; warnings do not.

**92 errors** · **47 warnings**

| Code | Severity | Summary |
| --- | --- | --- |
| [`E_ACC_PLACEMENT`](#e_acc_placement) | error | `accTitle`/`accDescr` used outside the plan level. |
| [`E_ARC_RADIUS`](#e_arc_radius) | error | Arc radius too small for its chord. |
| [`E_ARGCOUNT`](#e_argcount) | error | Component called with the wrong number of arguments. |
| [`E_ARITY`](#e_arity) | error | Built-in function called with the wrong number of arguments. |
| [`E_ASSIGN_UNDEF`](#e_assign_undef) | error | Assignment to an undeclared name. |
| [`E_ATTACH_POS_RANGE`](#e_attach_pos_range) | error | Opening attachment position is out of range. |
| [`E_ATTACH_WALL_REF`](#e_attach_wall_ref) | error | Opening attached to an unknown or ambiguous wall. |
| [`E_CALL_DEPTH`](#e_call_depth) | error | Value-function call stack too deep. |
| [`E_COLUMN_SIZE`](#e_column_size) | error | Column must have a positive size. |
| [`E_DIM_CURVE_REF`](#e_dim_curve_ref) | error | Invalid `dim radius`/`dim diameter` reference. |
| [`E_DIV_ZERO`](#e_div_zero) | error | Division or modulo by zero. |
| [`E_DOMAIN`](#e_domain) | error | Math domain error. |
| [`E_DOOR_KIND_CLAUSE`](#e_door_kind_clause) | error | That clause is not available on this kind of door. |
| [`E_DOOR_KIND_CURVED`](#e_door_kind_curved) | error | A non-hinged door kind cannot sit on a curved wall. |
| [`E_DOOR_OPEN_RANGE`](#e_door_open_range) | error | `open` must be between 0 and 1. |
| [`E_DOOR_WIDTH`](#e_door_width) | error | Door must have a positive width. |
| [`E_DOTTED_DECL`](#e_dotted_decl) | error | A dotted name cannot be declared. |
| [`E_DUP_ID`](#e_dup_id) | error | Duplicate element id. |
| [`E_DUP_INSTANCE`](#e_dup_instance) | error | Duplicate `place … as <name>` instance name. |
| [`E_FENCE_CURVED`](#e_fence_curved) | error | A fence cannot have a curved (`arc`) edge. |
| [`E_FURN_AGAINST`](#e_furn_against) | error | Invalid `against wall` fixture placement. |
| [`E_FURN_FLUSH`](#e_furn_flush) | error | `flush` on a placement that touches no edge. |
| [`E_FURN_ROOM`](#e_furn_room) | error | Furniture placed `in` an unknown room. |
| [`E_FURN_ROTATE`](#e_furn_rotate) | error | Furniture rotation must be a quarter-turn. |
| [`E_FURN_SIZE`](#e_furn_size) | error | Furniture must have a positive size. |
| [`E_HEIGHT_RANGE`](#e_height_range) | error | A height is outside the range a storey can be built at. |
| [`E_IMPORT_BAD_SPEC`](#e_import_bad_spec) | error | Malformed import spec. |
| [`E_IMPORT_CONFLICT`](#e_import_conflict) | error | Imported name conflicts with an existing component. |
| [`E_IMPORT_CYCLE`](#e_import_cycle) | error | Cyclic import. |
| [`E_IMPORT_NOT_EXPORTED`](#e_import_not_exported) | error | Imported name is not exported by the module. |
| [`E_IMPORT_NOT_FOUND`](#e_import_not_found) | error | Import path could not be resolved. |
| [`E_IMPORT_PARSE`](#e_import_parse) | error | Imported module has a parse error. |
| [`E_INDEX`](#e_index) | error | Array index out of range. |
| [`E_INTENT_NO_DOOR`](#e_intent_no_door) | error | The plan has no modeled entrance, so `reachable` cannot hold. |
| [`E_INTENT_NO_SITE`](#e_intent_no_site) | error | An intent asserts a SYMBOLIC window facing against a plan with no `site`. |
| [`E_INTENT_NO_WINDOW`](#e_intent_no_window) | error | A room the brief wants a window in has too few. |
| [`E_INTENT_NOT_ADJACENT`](#e_intent_not_adjacent) | error | Two rooms the brief wants adjacent share no interior door. |
| [`E_INTENT_ROOM_AREA`](#e_intent_room_area) | error | A named room's floor area is outside the brief's band. |
| [`E_INTENT_ROOM_COUNT`](#e_intent_room_count) | error | The plan's room count does not match the brief. |
| [`E_INTENT_ROOM_MISSING`](#e_intent_room_missing) | error | A room the brief names is absent from the plan. |
| [`E_INTENT_TOTAL_AREA`](#e_intent_total_area) | error | The plan's total floor area is outside the brief's band. |
| [`E_INTENT_UNREACHABLE`](#e_intent_unreachable) | error | A room cannot be reached from the entrance through modeled doors. |
| [`E_JSON_KIND`](#e_json_kind) | error | Unknown element kind in plan JSON. |
| [`E_JSON_SCHEMA`](#e_json_schema) | error | Plan JSON does not match the schema. |
| [`E_LAYOUT_CYCLE`](#e_layout_cycle) | error | Relational room placement forms a cycle. |
| [`E_LAYOUT_REF`](#e_layout_ref) | error | Relational placement references an unknown room. |
| [`E_LEVEL_DUP`](#e_level_dup) | error | Two `level` blocks declare the same storey number. |
| [`E_LEVEL_MIX`](#e_level_mix) | error | A drawable statement sits beside `level` blocks. |
| [`E_LEVEL_NEST`](#e_level_nest) | error | `level` used inside a block or component. |
| [`E_OPENING_ABOVE_WALL`](#e_opening_above_wall) | error | An opening's head is above the wall it is cut in. |
| [`E_OPENING_WIDTH`](#e_opening_width) | error | Opening must have a positive width. |
| [`E_OUTDOOR_POLY_DEGENERATE`](#e_outdoor_poly_degenerate) | error | An outdoor ring is degenerate, or a balcony was given one. |
| [`E_OUTDOOR_POLY_SELF_INTERSECT`](#e_outdoor_poly_self_intersect) | error | An outdoor ring crosses itself. |
| [`E_OUTDOOR_RAIL`](#e_outdoor_rail) | error | A `rail` clause on something that is not a balcony, or an unknown edge word. |
| [`E_OUTDOOR_SIZE`](#e_outdoor_size) | error | An outdoor surface must have a positive size. |
| [`E_PARSE`](#e_parse) | error | The source could not be read: its SHAPE is wrong. |
| [`E_PLACE_POLY`](#e_place_poly) | error | A rectangle-only placement clause aimed at a polygon room. |
| [`E_PLACE_REF`](#e_place_ref) | error | Furniture placed in an unknown or non-absolute room. |
| [`E_PNG_DEPENDENCY`](#e_png_dependency) | error | PNG/PDF export needs an optional dependency that is not installed. |
| [`E_RANGE_LIMIT`](#e_range_limit) | error | Range too large. |
| [`E_RECURSION`](#e_recursion) | error | Component recursion too deep. |
| [`E_REDEF`](#e_redef) | error | Name already defined in this scope. |
| [`E_ROOF_AMBIGUOUS`](#e_roof_ambiguous) | error | `roof overhang` cannot tell which wall ring to follow. |
| [`E_ROOF_CURVED`](#e_roof_curved) | error | `roof overhang` on a wall with a curved edge. |
| [`E_ROOF_OVERHANG`](#e_roof_overhang) | error | `roof overhang` is zero or negative. |
| [`E_ROOF_PLACEMENT`](#e_roof_placement) | error | `roof` used inside a component body. |
| [`E_ROOF_POLY_DEGENERATE`](#e_roof_poly_degenerate) | error | `roof polygon` outline has fewer than 3 effective vertices. |
| [`E_ROOF_SELF_INTERSECT`](#e_roof_self_intersect) | error | The roof outline crosses itself. |
| [`E_ROOF_WALL`](#e_roof_wall) | error | `roof overhang … wall <id>` names a wall that cannot carry a roof. |
| [`E_ROOM_ALIGN`](#e_room_align) | error | Unknown relational alignment edge. |
| [`E_ROOM_ALIGN_AXIS`](#e_room_align_axis) | error | Relational alignment edge belongs to the other axis. |
| [`E_ROOM_POLY_DEGENERATE`](#e_room_poly_degenerate) | error | Polygon room has fewer than three effective vertices. |
| [`E_ROOM_POLY_SELF_INTERSECT`](#e_room_poly_self_intersect) | error | Polygon room intersects itself. |
| [`E_ROOM_RADIUS`](#e_room_radius) | error | Circular room needs a positive radius. |
| [`E_ROOM_SIZE`](#e_room_size) | error | Room must have a positive size. |
| [`E_SILL_ABOVE_HEAD`](#e_sill_above_head) | error | A window's sill sits at or above its head. |
| [`E_SITE_BOUNDARY_DEGENERATE`](#e_site_boundary_degenerate) | error | The site `boundary` encloses no lot. |
| [`E_SITE_BOUNDARY_SELF_INTERSECT`](#e_site_boundary_self_intersect) | error | The site `boundary` crosses itself. |
| [`E_SITE_DUP`](#e_site_dup) | error | Two `site` blocks in one plan. |
| [`E_SITE_NO_STREET`](#e_site_no_street) | error | A `site` block declares no `street`. |
| [`E_STAIR_WIDTH`](#e_stair_width) | error | Stair flight `width` is outside the footprint. |
| [`E_STRIP_NEST`](#e_strip_nest) | error | Illegal `strip` nesting. |
| [`E_STRIP_SIZE`](#e_strip_size) | error | Room in a `strip` is missing a size. |
| [`E_TYPE`](#e_type) | error | Type mismatch. |
| [`E_UNKNOWN_COMPONENT`](#e_unknown_component) | error | Unknown component. |
| [`E_UNKNOWN_FN`](#e_unknown_fn) | error | Unknown function. |
| [`E_UNKNOWN_REF`](#e_unknown_ref) | error | Unknown reference. |
| [`E_VERT_SIZE`](#e_vert_size) | error | Vertical circulation must have a positive size. |
| [`E_VOID_SIZE`](#e_void_size) | error | A floor void must have a positive size. |
| [`E_WALL_THICKNESS`](#e_wall_thickness) | error | Wall must have a positive thickness. |
| [`E_WHILE_LIMIT`](#e_while_limit) | error | `while` exceeded its iteration cap. |
| [`E_WINDOW_WIDTH`](#e_window_width) | error | Window must have a positive width. |
| [`W_ALIAS_MATCH`](#w_alias_match) | warning | A room's use was inferred from an indirect alias, not stated. |
| [`W_BALCONY_NO_DOOR`](#w_balcony_no_door) | warning | A balcony with no way onto it. |
| [`W_BATH_VIA_BEDROOM`](#w_bath_via_bedroom) | warning | Bathroom is reachable only through a bedroom. |
| [`W_BEDROOM_NO_WINDOW`](#w_bedroom_no_window) | warning | Bedroom has no window. |
| [`W_CIRCUITOUS_PATH`](#w_circuitous_path) | warning | A room is reached by a very roundabout path. |
| [`W_DIM_INSIDE`](#w_dim_inside) | warning | A hand-written dimension line lands inside the building. |
| [`W_DIM_NO_WALL`](#w_dim_no_wall) | warning | A `dim faces`/`dim clear` endpoint has no wall to measure to. |
| [`W_DIM_OVERLAP`](#w_dim_overlap) | warning | Two hand-written dimensions are drawn on top of each other. |
| [`W_DOOR_CLEARANCE`](#w_door_clearance) | warning | Door is narrower than the minimum clear width. |
| [`W_DOOR_NEAR_CORNER`](#w_door_near_corner) | warning | A door leaves less wall between its jamb and a corner than the wall is thick. |
| [`W_DOOR_OFF_WALL`](#w_door_off_wall) | warning | Door does not lie on any wall. |
| [`W_DOORWAY_BLOCKED`](#w_doorway_blocked) | warning | A doorway's landing is blocked. |
| [`W_DRAWING_OVERFLOW`](#w_drawing_overflow) | warning | The whole drawing does not fit the declared paper, even though the building does. |
| [`W_DUP_ACC_METADATA`](#w_dup_acc_metadata) | warning | Duplicate `accTitle`/`accDescr`. |
| [`W_EMPTY_PLAN`](#w_empty_plan) | warning | Empty plan. |
| [`W_FIXTURE_BACK_TO_ROOM`](#w_fixture_back_to_room) | warning | A fixture stands against a wall but faces the wrong way. |
| [`W_FIXTURE_FLOATING`](#w_fixture_floating) | warning | A plumbing/kitchen fixture is not against a wall. |
| [`W_FIXTURE_WRONG_ROOM`](#w_fixture_wrong_room) | warning | Fixture is not inside its declared room. |
| [`W_FURN_CLEARANCE`](#w_furn_clearance) | warning | A fixture's use-space is blocked. |
| [`W_FURNITURE_OVERLAP`](#w_furniture_overlap) | warning | Two pieces of furniture overlap. |
| [`W_FURNITURE_WALL_COLLISION`](#w_furniture_wall_collision) | warning | Furniture penetrates a wall. |
| [`W_GARAGE_TOO_NARROW`](#w_garage_too_narrow) | warning | A garage is too narrow to park in. |
| [`W_HATCH_SCALE`](#w_hatch_scale) | warning | Hatch scale must be positive; using 1. |
| [`W_IMPORT_EMPTY_FILE`](#w_import_empty_file) | warning | Whole-file import binds an empty component. |
| [`W_NO_ENTRANCE`](#w_no_entrance) | warning | The plan has no exterior door. |
| [`W_OPENING_OFF_WALL`](#w_opening_off_wall) | warning | Opening does not lie on any wall. |
| [`W_OUTDOOR_OVERLAPS_ROOM`](#w_outdoor_overlaps_room) | warning | A ground surface is laid over a room's floor. |
| [`W_PATH_TOO_NARROW`](#w_path_too_narrow) | warning | The walk to a room squeezes below a passable width. |
| [`W_POCKET_RUN`](#w_pocket_run) | warning | A pocket door has no wall to slide into. |
| [`W_ROOM_DISCONNECTED`](#w_room_disconnected) | warning | Room has no door — it can't be entered. |
| [`W_ROOM_LABEL_OUTSIDE`](#w_room_label_outside) | warning | A room's explicit label anchor falls outside the room. |
| [`W_ROOM_NO_CLEAR_PATH`](#w_room_no_clear_path) | warning | A room cannot be entered or crossed. |
| [`W_ROOM_NO_FIXTURE`](#w_room_no_fixture) | warning | Bathroom or kitchen has no fixtures. |
| [`W_ROOM_NOT_ENCLOSED`](#w_room_not_enclosed) | warning | Bathroom is not fully enclosed. |
| [`W_ROOM_NOT_EQUATOR_FACING`](#w_room_not_equator_facing) | warning | A habitable room has windows, but none faces the equator side. |
| [`W_ROOM_OVERLAP`](#w_room_overlap) | warning | Rooms overlap. |
| [`W_ROOM_TOO_SMALL`](#w_room_too_small) | warning | Room is implausibly small. |
| [`W_ROOM_UNREACHABLE`](#w_room_unreachable) | warning | Room cannot be reached from the entrance. |
| [`W_SANITIZED_CONFIG`](#w_sanitized_config) | warning | A disallowed config value was stripped. |
| [`W_SCALE_OVERFLOW`](#w_scale_overflow) | warning | The drawing does not fit the declared paper at the declared scale. |
| [`W_STAIR_UNMATCHED`](#w_stair_unmatched) | warning | A run of vertical circulation appears on only one storey. |
| [`W_SWING_OBSTRUCTED`](#w_swing_obstructed) | warning | Door swing is obstructed. |
| [`W_SWING_ROOM_NOT_ADJACENT`](#w_swing_room_not_adjacent) | warning | `swing into <room>` names a room the door does not border. |
| [`W_UNKNOWN_MATERIAL`](#w_unknown_material) | warning | Unknown wall material; using the default hatch. |
| [`W_UNKNOWN_STYLE_KEY`](#w_unknown_style_key) | warning | Unknown style key. |
| [`W_UNKNOWN_THEME_KEY`](#w_unknown_theme_key) | warning | Unknown theme key. |
| [`W_WINDOW_OFF_WALL`](#w_window_off_wall) | warning | Window does not lie on any wall. |

## E_ACC_PLACEMENT

*error* — `accTitle`/`accDescr` used outside the plan level.

**Cause.** An `accTitle` or `accDescr` accessibility-metadata statement appeared inside a component or a control-flow block. They describe the whole plan, so they are only legal as direct plan-level statements.

**Fix.** Move the `accTitle`/`accDescr` line up to the plan body, alongside `units`/`north`.

```arch static
component c() { accDescr "x" }   # error: only allowed at plan level
```

## E_ARC_RADIUS

*error* — Arc radius too small for its chord.

**Cause.** An `arc (x,y) radius R` edge asks for a circle of radius R through the previous vertex and this one, but R is less than half the straight-line distance between them (or is not positive) — no such circle exists.

**Fix.** Raise the radius to at least half the chord (the message states the minimum), or move the endpoints closer together. `arch fix` applies the minimum for you.

```arch static
wall exterior thickness 200 { (0,0) arc (10000,0) radius 3000 }   # error: needs R >= 5000
```

## E_ARGCOUNT

*error* — Component called with the wrong number of arguments.

**Cause.** A component instance supplies more or fewer arguments than the component declares parameters.

**Fix.** Pass exactly one argument per declared parameter.

```arch static
component bed(x, y) { … }
bed(300)        # error: expects 2 arguments
```

## E_ARITY

*error* — Built-in function called with the wrong number of arguments.

**Cause.** A built-in (e.g. abs, sqrt, len) was called with the wrong argument count.

**Fix.** Check the function's arity; most built-ins take one argument.

```arch static
let x = abs(1, 2)   # error: abs expects 1 argument
```

## E_ASSIGN_UNDEF

*error* — Assignment to an undeclared name.

**Cause.** `NAME = value` was used for a name never introduced with `let`.

**Fix.** Declare it first with `let`, or fix a typo in the name.

```arch static
x = 5           # error: declare with `let x = …` first
```

## E_ATTACH_POS_RANGE

*error* — Opening attachment position is out of range.

**Cause.** The `at <pos>` of an attached opening is outside the host wall: a percentage outside 0–100%, a millimetre distance outside `0 … wall length`, or — since the position may be an expression — a value that is not a finite number at all.

**Fix.** Use a percentage in 0–100%, a millimetre distance within the wall's run, or `center`. The non-finite case carries no fix: there is no nearest legal value to clamp to, so check the expression that produced it.

```arch static
door on w1 at 150% width 900   # error: 150% is past the wall end
```

## E_ATTACH_WALL_REF

*error* — Opening attached to an unknown or ambiguous wall.

**Cause.** A `door`/`window`/`opening … on <wall> at <pos>` names a wall id (or category) that no wall has, or one that matches more than one wall — so the compiler cannot pick the polyline to walk.

**Fix.** Reference an existing, unique wall id (add `id=` to the wall if needed).

```arch static
door on w1 at 40% width 900   # error if no wall id=w1 (or several match)
```

## E_CALL_DEPTH

*error* — Value-function call stack too deep.

**Cause.** A value-function recurses (directly or mutually) beyond the call-depth limit.

**Fix.** Make the recursion terminate, or rewrite it iteratively with a bounded `while`.

```arch static
let f(n) = f(n + 1)   # error: never terminates
```

## E_COLUMN_SIZE

*error* — Column must have a positive size.

**Cause.** A column's width or height evaluated to zero or a negative number.

**Fix.** Give the column a positive `size W x H`.

```arch static
column at (0,0) size 0x300   # error: width is 0
```

## E_DIM_CURVE_REF

*error* — Invalid `dim radius`/`dim diameter` reference.

**Cause.** A curve call-out references an element that does not exist, is not the right shape (a `diameter` on a rectangular room, a `radius` on a wall with no `arc` edge), matches several walls, or omits `segment <n>` on a wall with more than one arc. The compiler will not guess which curve was meant.

**Fix.** Name an existing, unique wall id whose edge is an `arc` (adding `segment <n>` when it has several), or an existing `room circle` id for `diameter`.

```arch static
dim radius w1   # error if w1 is straight, unknown, or has two arc edges
```

## E_DIV_ZERO

*error* — Division or modulo by zero.

**Cause.** An expression divides (or takes a remainder) by a value that evaluates to zero.

**Fix.** Guard the divisor, or use a non-zero value.

```arch static
let x = 10 / 0   # error
```

## E_DOMAIN

*error* — Math domain error.

**Cause.** A built-in received an out-of-domain argument (e.g. `sqrt` of a negative number).

**Fix.** Pass a value within the function's domain.

```arch static
let x = sqrt(-1)   # error
```

## E_DOOR_KIND_CLAUSE

*error* — That clause is not available on this kind of door.

**Cause.** A `door` statement carries a clause its kind has no meaning for: `hinge` on any of the sliding family (`sliding`/`barn`/`bifold`/`pocket` — nothing is hinged to a jamb), `swing` on a `sliding` or `pocket` door (the panel stays in the plane of the wall, or inside it), or `slide`/`open` on a `hinged` door (nothing travels along the wall). ArchLang refuses rather than draws the statement as if the clause were absent — silently ignoring a written clause is the silent-error design this project rules out (the v1.23 precedent: rectangle-only clauses REFUSE a polygon room rather than approximate it).

**Fix.** Delete the clause (the machine-applicable fix does exactly that), or change the door's kind to one the clause belongs to. `swing` on a `barn` or `bifold` door is legal and means which FACE of the wall the panel hangs on or folds toward — it is not a leaf arc.

```arch static
door pocket on w1 at 50% width 900 hinge left   # error: a pocket door has no hinge
```

## E_DOOR_KIND_CURVED

*error* — A non-hinged door kind cannot sit on a curved wall.

**Cause.** The panel of a `sliding`, `barn`, `bifold` or `pocket` door is a straight rectangle running along a straight track, and its pocket/track geometry is straight by construction — none of that survives on a wall whose hosting edge is an `arc`. A hinged leaf does survive (its swing is taken from the tangent at the doorway), so only the sliding family is refused. Refusing is deliberate: drawing a straight panel across a curved reveal would be a wrong drawing with no diagnostic.

**Fix.** Use a `hinged` door on the curve (the default — delete the kind word), or move the door onto one of the wall's straight runs.

```arch static
wall w1 thickness 200 { (0,0) arc (6000,0) radius 4000 }
door pocket on w1 at 50% width 900   # error: curved host
```

## E_DOOR_OPEN_RANGE

*error* — `open` must be between 0 and 1.

**Cause.** `open <f>` is the fraction of its travel a sliding-family panel is DRAWN at — 0 is closed, 1 fully open — so a value outside `[0,1]` names a position the panel cannot reach. It is a drawing fact only: nothing measured (`lint`, `describe`, the intent channel) reads it, so a half-open door can never silence a clearance warning.

**Fix.** Give `open` a fraction in `[0,1]`; `arch fix` clamps it to the nearer end for you. Omit it entirely for the default 0.5.

```arch static
door sliding on w1 at 50% width 1200 open 1.5   # error: outside [0,1]
```

## E_DOOR_WIDTH

*error* — Door must have a positive width.

**Cause.** A door's width evaluated to zero or a negative number.

**Fix.** Give the door a positive `width`.

```arch static
door at (0,0) width 0   # error
```

## E_DOTTED_DECL

*error* — A dotted name cannot be declared.

**Cause.** A dotted name like `west.main` addresses an element INSIDE a `place`d instance. The namespace belongs to the `place`, so a dotted name is only ever a reference — it can never be declared with `id=`, `let`, `as`, or `for`.

**Fix.** Declare the short name and address it from outside as `<instance>.<name>`.

```arch static
room id=main at (0,0) size 3000x3000   # then, in the plan: furniture bed in west.main centered
```

## E_DUP_ID

*error* — Duplicate element id.

**Cause.** Two elements declare the same `id=…`; ids must be unique across the plan.

**Fix.** Rename one of them, or drop the explicit id to auto-generate a unique one.

```arch static
room id=a at (0,0) size 1x1
room id=a at (1,0) size 1x1   # error: duplicate id "a"
```

## E_DUP_INSTANCE

*error* — Duplicate `place … as <name>` instance name.

**Cause.** Two `place` statements claim the same instance name. The name is the id NAMESPACE for everything inside the instance, so reusing it would silently merge two instances' ids.

**Fix.** Give each instance its own name (`as west` / `as east`).

```arch static
place wing() as west at (0,0)
place wing() as west at (9000,0)   # error: instance "west" already used
```

## E_FENCE_CURVED

*error* — A fence cannot have a curved (`arc`) edge.

**Cause.** An `arc … radius …` clause appeared inside a `fence { … }` body. The post pitch, the panel offset and the reported `length_mm` are all measured along a STRAIGHT run, so a faceted curve would silently measure the facets rather than the arc — this release refuses rather than approximating (the same decision `roof` makes with `E_ROOF_CURVED`).

**Fix.** Approximate the curve with short straight runs, which is what a fence built on a curve actually is.

```arch static
fence picket { (0,0) arc (3000,3000) radius 3000 }   # error: curved edge
```

## E_FURN_AGAINST

*error* — Invalid `against wall` fixture placement.

**Cause.** A wall-anchored fixture references an unknown wall, omits `segment` on a multi-segment wall, omits `side`, sits on a non-axis-aligned segment, has an out-of-range offset, or also sets `rotate`. The compiler will not guess which wall/side/segment was meant.

**Fix.** Name an existing wall id, add `segment <n>` for multi-segment walls, give `side left|right`, keep the segment axis-aligned, and drop any explicit `rotate`.

```arch static
furniture wc against wall w1 side left size 400x700   # error if w1 is unknown or multi-segment
```

## E_FURN_FLUSH

*error* — `flush` on a placement that touches no edge.

**Cause.** `flush` measures `inset` from the inner face of the wall behind an *anchored edge*. A `centered` piece (or the equivalent `anchor center`) is pushed against no edge, so there is no wall face to measure from.

**Fix.** Anchor the piece to the edge you want it flush with (`anchor bottom flush`, `anchor top-left flush`), or drop `flush` and keep it centred.

```arch static
furniture wc in bath centered flush size 400x700   # error: centred, so nothing to be flush with
```

## E_FURN_ROOM

*error* — Furniture placed `in` an unknown room.

**Cause.** A furniture item names a room with `in <roomId>`, but no room has that id.

**Fix.** Use the id of an existing `room id=…`, or drop the `in` clause.

```arch static
furniture bed at (0,0) size 1500x2000 in bedrm   # error: no room id=bedrm
```

## E_FURN_ROTATE

*error* — Furniture rotation must be a quarter-turn.

**Cause.** A furniture item's `rotate` is not one of 0, 90, 180, or 270 degrees.

**Fix.** Use a quarter-turn: `rotate 0|90|180|270`.

```arch static
furniture wc at (0,0) size 400x700 rotate 45   # error: not a quarter-turn
```

## E_FURN_SIZE

*error* — Furniture must have a positive size.

**Cause.** A furniture item's width or height evaluated to zero or a negative number.

**Fix.** Give the item a positive `size W x H`.

```arch static
furniture bed at (0,0) size 0x2000   # error
```

## E_HEIGHT_RANGE

*error* — A height is outside the range a storey can be built at.

**Cause.** A `height`, `sill` or `head` clause evaluated to a number no building has: zero or negative, not finite, or above 100 m. A wall or a head at zero is not a low one, it is a missing one. A window `sill` of exactly 0 is the one exception and is legal — it means a floor-length window. **The range guards the ENDS, not the units:** heights are millimetres like every other number in the language, so `height 3` is three millimetres, is inside the range, and is accepted — the compiler will not guess that three metres was meant, because that would put a number in the datum the author never wrote.

**Fix.** Write the height in millimetres, or drop the clause and inherit the default (the fix does exactly that) — 3000 for a storey, 2100 for a door or window head, 900 for a window sill.

```arch static
wall id=w1 exterior thickness 200 height 0 { (0,0) (4000,0) close }   # error: a wall with no height
```

## E_IMPORT_BAD_SPEC

*error* — Malformed import spec.

**Cause.** The string after `import` is not a recognizable module reference.

**Fix.** Use a relative path ("lib/x.arch") or a namespaced spec ("@scope/name:1.0.0").

```arch static
import "???" : a   # error
```

## E_IMPORT_CONFLICT

*error* — Imported name conflicts with an existing component.

**Cause.** An imported component has the same name as one already defined or imported.

**Fix.** Rename with `as`, or remove the duplicate.

```arch static
import "lib.arch": bed as lib_bed
```

## E_IMPORT_CYCLE

*error* — Cyclic import.

**Cause.** Modules import each other in a cycle, which cannot be resolved.

**Fix.** Break the cycle so module dependencies form a tree.

```arch static
# a.arch imports b.arch which imports a.arch  → error
```

## E_IMPORT_NOT_EXPORTED

*error* — Imported name is not exported by the module.

**Cause.** The module has no component with the requested name.

**Fix.** Import a name the module actually defines (check its `component`s).

```arch static
import "lib.arch": nope   # error if lib.arch has no `component nope`
```

## E_IMPORT_NOT_FOUND

*error* — Import path could not be resolved.

**Cause.** The World could not read the module at the given path.

**Fix.** Check the path (relative to the importing file) and that the file exists.

```arch static
import "lib/missing.arch": a   # error
```

## E_IMPORT_PARSE

*error* — Imported module has a parse error.

**Cause.** The module referenced by `import` does not itself parse.

**Fix.** Fix the syntax error in the imported module.

```arch static
# error originates in the imported file
```

## E_INDEX

*error* — Array index out of range.

**Cause.** `arr[i]` used an index outside `0 .. len(arr) - 1`.

**Fix.** Clamp or check the index against `len(arr)`.

```arch static
let a = [1, 2]
let x = a[5]   # error
```

## E_INTENT_NO_DOOR

*error* — The plan has no modeled entrance, so `reachable` cannot hold.

**Cause.** An intent asserts `reachable: true`, but the plan has no door connecting a room to the exterior — nothing is enterable, so no room is reachable.

**Fix.** Add an exterior entrance `door` on a perimeter wall. Advisory tier: reported and scored by `validateIntent` but does NOT fail `ok` (gate: false).

```arch static
door on exterior at 50% width 900   # a front door
```

## E_INTENT_NO_SITE

*error* — An intent asserts a SYMBOLIC window facing against a plan with no `site`.

**Cause.** An intent `roomsInclude[].windows.facing` names one of the derived directions (`street`, `back`, `equator_side`, `sunrise_side`, `sunset_side`) but the plan declares no `site` block, so there is nothing to resolve the name against. The question is unanswerable, not answered no.

**Fix.** Declare `site { street … }` in the plan (that is what gives the derived names their letters), or assert a plain compass letter instead. Gating tier: this failure fails `validateIntent`'s `ok` — it is a refusal, never a silent pass and never a silent miss.

```arch static
plan "H" {
  units mm
  site { street south }   # now `facing: "equator_side"` resolves to "S"
}
```

## E_INTENT_NO_WINDOW

*error* — A room the brief wants a window in has too few.

**Cause.** An intent `roomsInclude[].windows` requires at least one window in a concept's room(s) (e.g. "give the bedroom a window"), but the plan places fewer than the required count.

**Fix.** Add a `window` on one of that room's walls. Gating tier: this failure DOES fail `validateIntent`'s `ok`.

```arch static
window on north at 40% width 1200   # light the bedroom
```

## E_INTENT_NOT_ADJACENT

*error* — Two rooms the brief wants adjacent share no interior door.

**Cause.** An intent `adjacency` edge names two concepts (e.g. hall ↔ bathroom) that are not joined by an interior door or cased opening in the plan's modeled connectivity.

**Fix.** Add a `door` (or `opening`) on the wall the two rooms share so they are directly connected. Advisory tier: this is scored and reported by `validateIntent` but does NOT fail `ok` (gate: false) — one-shot topology is what the loop tools address.

```arch static
door on wall_hall_bath width 800   # connect the hall to the bathroom
```

## E_INTENT_ROOM_AREA

*error* — A named room's floor area is outside the brief's band.

**Cause.** An intent `roomsInclude[].areaM2` gives a per-room area band (from a number in the brief, ±10% for "about N"), but the matched room's area falls outside it.

**Fix.** Resize the room so its floor area lands in the band. Gating tier: this failure fails `validateIntent`'s `ok`. Assert a band only where the brief states a number — qualitative size words license none.

```arch static
room at (0,0) size 4000x3000 label "Bedroom"   # 12 m²
```

## E_INTENT_ROOM_COUNT

*error* — The plan's room count does not match the brief.

**Cause.** An intent `rooms` count (asserted only when the brief ENUMERATES its rooms) does not match the plan's room total. A single surplus room passes only when it is pure circulation (a hall/corridor) — policy B.

**Fix.** Add or remove rooms to reach the enumerated count. Gating tier: this failure fails `validateIntent`'s `ok`.

```arch static
# brief lists 4 rooms; the plan draws 5 (and the extra is a bedroom, not a hall)
```

## E_INTENT_ROOM_MISSING

*error* — A room the brief names is absent from the plan.

**Cause.** An intent `roomsInclude[]` concept (e.g. "bathroom") matched no room by label, `room_type`, or `uses` — the plan is missing a room the brief asked for.

**Fix.** Add a `room` whose label, `uses`, or type matches the concept. Gating tier: this failure fails `validateIntent`'s `ok`.

```arch static
room at (0,0) size 2000x2000 label "Bathroom" uses bath   # supply the missing room
```

## E_INTENT_TOTAL_AREA

*error* — The plan's total floor area is outside the brief's band.

**Cause.** An intent `totalAreaM2` band (from a number in the brief, ±10% for "about N") does not contain the plan's total floor area.

**Fix.** Grow or shrink rooms so the total lands in the band. Gating tier: this failure fails `validateIntent`'s `ok`. Assert a band only where the brief states a number.

```arch static
# brief says "about 42 m²" (band 37.8–46.2); the plan totals 52 m²
```

## E_INTENT_UNREACHABLE

*error* — A room cannot be reached from the entrance through modeled doors.

**Cause.** An intent asserts `reachable: true` and the plan HAS an entrance, but one or more rooms are cut off — no chain of modeled doors reaches them from the exterior.

**Fix.** Add interior doors so every room connects back to the entrance. Advisory tier: reported and scored by `validateIntent` but does NOT fail `ok` (gate: false).

```arch static
door on wall_hall_store width 800   # connect the isolated room
```

## E_JSON_KIND

*error* — Unknown element kind in plan JSON.

**Cause.** An element in the JSON names a `kind` (or lives in an array) the builder does not recognize — e.g. an opening whose `kind` is not `door`, `window`, or `opening`.

**Fix.** Use one of the supported kinds: opening `kind` must be `door` | `window` | `opening`.

```arch static
{ "openings": [ { "kind": "portal", "width": 900 } ] }   # error at /openings/0/kind: unknown kind "portal"
```

## E_JSON_SCHEMA

*error* — Plan JSON does not match the schema.

**Cause.** A value passed to `planFromJson` has the wrong shape or type for its JSON path (e.g. a room missing a numeric `width`, a non-array `rooms`), or it uses a construct the JSON form cannot represent — scripting (`let`/`for`/`if`/`component`) and `import` are intentionally not supported.

**Fix.** Fix the value at the reported JSON path (the message names it, e.g. `/rooms/0/width`); express geometry as concrete numbers, and author scripting/imports in `.arch` source instead.

```arch static
{ "rooms": [ { "x": 0, "y": 0, "width": "big", "height": 3000 } ] }   # error at /rooms/0/width: expected a number
```

## E_LAYOUT_CYCLE

*error* — Relational room placement forms a cycle.

**Cause.** Rooms placed with `right-of`/`below`/… reference each other in a loop, so no order resolves them.

**Fix.** Break the cycle by giving one of the rooms absolute `at (x,y)` coordinates.

```arch static
room id=a right-of b size 100x100
room id=b left-of a size 100x100   # error: a ↔ b cycle
```

## E_LAYOUT_REF

*error* — Relational placement references an unknown room.

**Cause.** A `right-of`/`below`/… clause names a room id that does not exist in the plan.

**Fix.** Reference an existing room id, or fix the typo.

```arch static
room id=k right-of ghost size 100x100   # error: no room "ghost"
```

## E_LEVEL_DUP

*error* — Two `level` blocks declare the same storey number.

**Cause.** Level numbers identify the storeys of one building, so each may be declared once; two `level 1` blocks would produce two pages both claiming to be level 1.

**Fix.** Renumber one of them, or merge the two bodies into a single `level` block.

```arch static
level 1 { room at (0,0) size 3000x3000 }
level 1 { room at (0,0) size 3000x3000 }   # error: level 1 twice
```

## E_LEVEL_MIX

*error* — A drawable statement sits beside `level` blocks.

**Cause.** A plan is either single-storey (no `level` block) or entirely made of them: anything that draws belongs to exactly one storey, so a room/wall/door/`for`/`strip`/component call at plan level next to a `level` block has no storey to belong to.

**Fix.** Move the statement inside the `level` block it belongs to. Only settings (`units`/`grid`/`paper`/`scale`/`north`/`site`/`dims`/`title`/`axes`/`schedule`/`legend`), `component`/`import` declarations, and the plan-global `let`/`set` stay outside — they apply to every level.

```arch static
plan "H" {
  room at (0,0) size 3000x3000   # error: move it into a level
  level 1 { wall exterior thickness 200 { (0,0) (3000,0) close } }
}
```

## E_LEVEL_NEST

*error* — `level` used inside a block or component.

**Cause.** A `level` block is a plan-level statement: it partitions the whole plan into storeys, so it cannot be nested inside another `level`, a `for`/`if`/`while` body, a `strip`, or a component definition.

**Fix.** Move the `level` block out to the plan body. To draw the same content on several storeys, put it in a `component` and call it from each level.

```arch static
component c() { level 1 { } }   # error: only allowed at plan level
```

## E_OPENING_ABOVE_WALL

*error* — An opening's head is above the wall it is cut in.

**Cause.** A `door`/`window`/`opening` whose `head` exceeds its host wall's own `height` would run out through the top of the wall. The wall's height is the authored `wall … height`, else this storey's, else the 3000 mm default — so this fires on a low parapet or a short storey as readily as on a mistyped head.

**Fix.** Lower the `head` to the wall's height (the fix does exactly that), or raise the wall with `wall … height`.

```arch static
wall id=w1 exterior thickness 200 height 2200 { (0,0) (4000,0) close }
  door id=d on w1 at 50% width 900 head 2400   # error: head above a 2200 wall
```

## E_OPENING_WIDTH

*error* — Opening must have a positive width.

**Cause.** A cased opening's width evaluated to zero or a negative number.

**Fix.** Give the opening a positive `width`.

```arch static
opening at (0,0) width 0   # error
```

## E_OUTDOOR_POLY_DEGENERATE

*error* — An outdoor ring is degenerate, or a balcony was given one.

**Cause.** Either an `outdoor … polygon …` ring has fewer than 3 effective vertices once duplicate and collinear points are removed (so it encloses no area), or the ring was written on a `balcony`, which is rectangle-only: a balcony's railing is derived per EDGE — top, bottom, left, right — and a ring has no such edges.

**Fix.** Give the ring at least 3 corners that are not all on one line. For a balcony, use `at (x,y) size WxH`; a polygonal balcony is deferred by name, not supported.

```arch static
outdoor paving polygon (0,0) (1000,0) (2000,0)   # error: all three are collinear
```

## E_OUTDOOR_POLY_SELF_INTERSECT

*error* — An outdoor ring crosses itself.

**Cause.** The vertices of an `outdoor … polygon …` describe a ring whose edges intersect (a bow-tie), so it encloses no single area — there is no unambiguous surface to hatch or to measure. Usually two vertices in the wrong order.

**Fix.** Reorder the vertices so the ring is a simple polygon, walking the outline once without crossing back over itself.

```arch static
outdoor deck polygon (0,0) (4000,4000) (4000,0) (0,4000)   # error: a bow-tie
```

## E_OUTDOOR_RAIL

*error* — A `rail` clause on something that is not a balcony, or an unknown edge word.

**Cause.** `rail` names which edges carry a railing, and only a `balcony` has one. Written on any other ground surface it has no meaning — and a clause with no meaning is refused rather than silently dropped, so a plan never draws differently from what it says. The same code covers an edge word outside `top|bottom|left|right|all|none`.

**Fix.** Delete the clause, or make the surface an `outdoor balcony`. For an unknown word, use one of the six edge words.

```arch static
outdoor deck at (0,0) size 3000x2000 rail all   # error: a deck has no railing
```

## E_OUTDOOR_SIZE

*error* — An outdoor surface must have a positive size.

**Cause.** An `outdoor … at (x,y) size WxH` statement's width or height evaluated to zero or a negative number, so there is no ground to draw, hatch or measure.

**Fix.** Give it a positive `size W x H` — the surface's extent in plan. For a shape that is not a rectangle, use the `polygon` spelling instead.

```arch static
outdoor lawn at (0,0) size 0x4000   # error: zero width
```

## E_PARSE

*error* — The source could not be read: its SHAPE is wrong.

**Cause.** The lexer or the parser could not make a statement out of the bytes at this span — a missing or misspelled keyword, a value where a keyword belongs, an unterminated string, an unbalanced brace, clauses written in the wrong order. It is the one code that says nothing about what the plan MEANS: resolution never ran here, so no measurement, no geometry and no soundness rule had a chance to speak.

**Fix.** Read the message: it names what was expected and what was found, at a byte span. Compare the statement against `arch spec`'s one line for that keyword — clause ORDER is part of the grammar, not a suggestion. Unlike every other code in this catalog, there is no machine-applicable fix to apply, because the compiler has no reading of the text to correct.

```arch static
door on w1 at 40% width 900 wall w1   # error: `wall` pairs with the `at (x,y)` form only
```

## E_PLACE_POLY

*error* — A rectangle-only placement clause aimed at a polygon room.

**Cause.** Relational room placement (`right-of`/`left-of`/`below`/`above`) and room-relative furniture placement (`in <room> centered|anchor …`) are closed-form arithmetic on a room's four RECTANGLE edges. A `room polygon` has no such edges, and its bounding box is not its floor — an anchor could land the piece in a notch that is outside the room — so the compiler refuses instead of guessing (ADR 0005).

**Fix.** Place the room or the fixture with explicit `at (x,y)` coordinates (a fixture may add `rotate`), or make the referenced room rectangular.

```arch static
room id=L polygon (0,0) (6000,0) (6000,4000) (3000,4000) (3000,6000) (0,6000)
furniture wc in L anchor bottom-left   # error: L is a polygon room
```

## E_PLACE_REF

*error* — Furniture placed in an unknown or non-absolute room.

**Cause.** A `furniture … in <room> centered|anchor …` names a room that does not exist, or one positioned relationally (`right-of`/…) whose box is not yet fixed when the fixture is placed.

**Fix.** Reference an existing room given absolute `at (x,y)` coordinates.

```arch static
furniture bed in bedrm centered size 1500x2000   # error: no room id=bedrm
```

## E_PNG_DEPENDENCY

*error* — PNG/PDF export needs an optional dependency that is not installed.

**Cause.** Rendering to PNG needs `@resvg/resvg-js` (PDF needs `pdfkit`); the optional binary is absent in this environment (it is not bundled, to keep the core zero-dependency).

**Fix.** Install the optional dependency (`npm install @resvg/resvg-js`), or re-run with `--install` to fetch it automatically, or render to SVG/DXF (zero-dependency).

```arch static
arch preview plan.arch --install   # fetches @resvg/resvg-js, then renders the PNG
```

## E_RANGE_LIMIT

*error* — Range too large.

**Cause.** A `lo..hi` range would expand to more elements than the safety cap allows.

**Fix.** Use a smaller range, or restructure to avoid materializing it.

```arch static
for i in 0..1000000 { … }   # error: range too large
```

## E_RECURSION

*error* — Component recursion too deep.

**Cause.** Component instantiation nested beyond the depth limit (usually unbounded self-instantiation).

**Fix.** Add a base case so the recursion terminates.

```arch static
component r(n) { r(n) }   # error: never terminates
```

## E_REDEF

*error* — Name already defined in this scope.

**Cause.** A `let` re-declares a name already bound in the same scope.

**Fix.** Rename one binding, or use `NAME = …` to reassign instead of redeclaring.

```arch static
let x = 1
let x = 2   # error: redefinition
```

## E_ROOF_AMBIGUOUS

*error* — `roof overhang` cannot tell which wall ring to follow.

**Cause.** The `overhang` sugar offsets ONE closed wall ring, and with no `wall <id>` clause it infers that ring as the plan's single closed `exterior` wall. The plan has none, or has more than one, so there is nothing to infer — and picking the first would silently draw a roof over part of the building.

**Fix.** Name the ring (`roof overhang 600 wall <id>`), or state the outline yourself with `roof polygon (x,y) (x,y) (x,y) …`.

```arch static
plan "H" {
  units mm
  room id=r1 at (0,0) size 4000x3000
  roof overhang 600   # error: no closed exterior wall to follow
}
```

## E_ROOF_CURVED

*error* — `roof overhang` on a wall with a curved edge.

**Cause.** The named ring has an `arc` edge. Offsetting a curve is a different construction from mitring two straight faces (concentric radii, plus the cases where the offset radius passes through zero), and faceting it would put a tessellation in the one place a reader is entitled to read a true parallel.

**Fix.** State the outline explicitly with `roof polygon …`, or follow a straight-edged wall ring instead.

```arch static
wall id=drum exterior thickness 200 { (0,0) arc (6000,0) radius 4000 (6000,4000) (0,4000) close }
roof overhang 600 wall drum   # error: arc edge
```

## E_ROOF_OVERHANG

*error* — `roof overhang` is zero or negative.

**Cause.** An overhang is a distance the roof projects PAST the wall's outer face, so it must be positive. Zero would draw the outline on the wall face (say nothing instead), and a negative value would draw it inside the building.

**Fix.** Give a positive projection in mm, e.g. `roof overhang 600`.

```arch static
roof overhang 0   # error: must be positive
```

## E_ROOF_PLACEMENT

*error* — `roof` used inside a component body.

**Cause.** A roof belongs to a building, not to a reusable part of one: `roof overhang` is offset from the plan's closed exterior wall ring, and a component is authored in its own local frame with no such ring. Two `place`d copies would each derive an outline and the plan would draw two roofs.

**Fix.** Move the `roof` line out to the plan body (or into the `level` block it belongs to).

```arch static
component wing() { roof overhang 600 }   # error: only allowed at plan level
```

## E_ROOF_POLY_DEGENERATE

*error* — `roof polygon` outline has fewer than 3 effective vertices.

**Cause.** After removing duplicate and straight-through (collinear) points the ring encloses no area, so it is a line or a point rather than a roof outline.

**Fix.** Give at least three vertices that actually turn a corner.

```arch static
roof polygon (0,0) (4000,0) (8000,0)   # error: all three are collinear
```

## E_ROOF_SELF_INTERSECT

*error* — The roof outline crosses itself.

**Cause.** Either the `roof polygon` ring was written as a bow-tie, or the derived `overhang` offset is wide enough to swallow a notch in the wall it follows (a 900 mm eave across a 600 mm light well) and the pushed-out faces cross. Neither encloses a single area.

**Fix.** Reorder the vertices, or reduce the overhang — or state the outline explicitly with `roof polygon …`.

```arch static
roof polygon (0,0) (4000,4000) (4000,0) (0,4000)   # error: bow-tie
```

## E_ROOF_WALL

*error* — `roof overhang … wall <id>` names a wall that cannot carry a roof.

**Cause.** The named wall is not declared in this plan, or it is not a closed ring. An overhang is offset from a loop; an open polyline has no inside to be outside of.

**Fix.** Check the id, and give the wall a `close` so its points form a ring.

```arch static
wall id=w1 exterior thickness 200 { (0,0) (8000,0) }
roof overhang 600 wall w1   # error: not a closed ring
```

## E_ROOM_ALIGN

*error* — Unknown relational alignment edge.

**Cause.** A relationally-placed room's `align <word>` names something that is not an alignment edge. The set is closed (`top`, `middle`, `bottom` for `right-of`/`left-of`; `left`, `center`, `right` for `below`/`above`), and an unrecognised word used to fall through to the leading edge silently — drawing the plan as if `align` had never been written.

**Fix.** Use one of the six edges; the diagnostic suggests the nearest one and carries a fix that rewrites just that word.

```arch static
room id=b right-of a align sideways size 3000x3000   # error: not an edge (did you mean `bottom`?)
```

## E_ROOM_ALIGN_AXIS

*error* — Relational alignment edge belongs to the other axis.

**Cause.** A relationally-placed room's `align <edge>` names a real edge, but not one of the axis that direction aligns on. A horizontal relation (`right-of`/`left-of`) offsets its room vertically, so it takes `top|middle|bottom`; a vertical relation (`below`/`above`) offsets horizontally, so it takes `left|center|right`. (`middle` and `center` are honoured on both — they are the same instruction spelled twice.) A cross-axis edge used to be ignored in silence, drawing the room against the leading edge exactly as if `align` had never been written — distinct from `E_ROOM_ALIGN`, where the word is not an edge at all.

**Fix.** Use the edge of the correct axis; the diagnostic names its exact counterpart (leading stays leading, trailing stays trailing) and carries a fix that rewrites just that word.

```arch static
room id=b right-of a align right size 3000x3000   # error: `right` is horizontal (did you mean `bottom`?)
```

## E_ROOM_POLY_DEGENERATE

*error* — Polygon room has fewer than three effective vertices.

**Cause.** After removing duplicate and straight-through (collinear) points, the ring encloses no area — so it has no floor, no centroid and no containment test.

**Fix.** Give the room at least three vertices that actually turn a corner.

```arch static
room polygon (0,0) (4000,0) (8000,0)   # error: all three are collinear
```

## E_ROOM_POLY_SELF_INTERSECT

*error* — Polygon room intersects itself.

**Cause.** Two of the ring's edges cross (or run along each other), so the polygon is not simple and which side is 'inside' is undefined — area, containment and adjacency would all be meaningless.

**Fix.** Reorder the vertices so the ring is traced once around the room without crossing itself (a bow-tie usually means two vertices are swapped).

```arch static
room polygon (0,0) (4000,4000) (4000,0) (0,4000)   # error: bow-tie
```

## E_ROOM_RADIUS

*error* — Circular room needs a positive radius.

**Cause.** A `room circle at (cx,cy) radius R` has R of zero or less, so it encloses no floor — there is no area, no centroid and no containment test to compute.

**Fix.** Give the room a positive radius in millimetres.

```arch static
room circle at (5000,5000) radius 0   # error: no floor
```

## E_ROOM_SIZE

*error* — Room must have a positive size.

**Cause.** A room's width or height evaluated to zero or a negative number.

**Fix.** Give the room a positive `size W x H`.

```arch static
room at (0,0) size 0x4000   # error: width is 0
```

## E_SILL_ABOVE_HEAD

*error* — A window's sill sits at or above its head.

**Cause.** A `window`'s `sill` is the bottom of the glazing and its `head` is the top, both measured from this storey's floor. When the sill is not below the head there is no opening between them — the statement describes a window with no glass in it. Refused rather than silently swapped: which of the two numbers is wrong is not something the compiler can know.

**Fix.** Lower the `sill` below the `head`, or raise the `head`. Dropping the `sill` clause (the fix) restores the 900 mm default.

```arch static
window id=w on wall1 at 50% width 1200 sill 2400 head 2100   # error: sill above head
```

## E_SITE_BOUNDARY_DEGENERATE

*error* — The site `boundary` encloses no lot.

**Cause.** A `site { boundary … }` ring has fewer than 3 effective vertices once duplicate and collinear points are removed, so there is no lot to draw a property line round or to report an area for.

**Fix.** Give the boundary at least 3 corners that are not all on one line.

```arch static
site { street north
  boundary (0,0) (10000,0) }   # error: two points is not a lot
```

## E_SITE_BOUNDARY_SELF_INTERSECT

*error* — The site `boundary` crosses itself.

**Cause.** The vertices of a `site { boundary … }` describe a ring whose edges intersect, so it encloses no single lot and `describe --json`'s `site.lot_area_m2` would have no honest value. Usually two vertices in the wrong order.

**Fix.** Reorder the vertices so the boundary walks the lot line once without crossing back over itself.

```arch static
site { street north
  boundary (0,0) (10000,10000) (10000,0) (0,10000) }   # error: a bow-tie
```

## E_SITE_DUP

*error* — Two `site` blocks in one plan.

**Cause.** A plan declares `site` more than once. `axes` merges when repeated because two axis lists *append*; two `street` values **contradict**, and silently keeping the last one would hide an authoring mistake in the very statement whose job is to be the single source of orientation.

**Fix.** Delete one of the blocks, or merge their fields into a single `site { … }`. The first block is the one that takes effect.

```arch static
plan "H" {
  units mm
  site { street south }
  site { street north }   # error: site already declared
}
```

## E_SITE_NO_STREET

*error* — A `site` block declares no `street`.

**Cause.** `street` is the only required field of `site`: every derived name (`back`, and the three `_side` names) is a function of it, so a site without one derives nothing at all. Refusing beats defaulting — there is no direction a building's frontage faces by default.

**Fix.** Add `street north`, `south`, `east` or `west` inside the block. `hemisphere` is the optional field (it defaults to `north`).

```arch static
plan "H" {
  units mm
  site { hemisphere south }   # error: no street
}
```

## E_STAIR_WIDTH

*error* — Stair flight `width` is outside the footprint.

**Cause.** A `stair`'s optional `width` is the FLIGHT width measured across the run, so it has to be positive and no wider than the footprint's cross-axis extent (the short side — the flight always runs along the long one). This value is not.

**Fix.** Drop `width` to fill the footprint, or give a value between 0 and the footprint's short side.

```arch static
stair id=s at (0,0) size 900x2600 dir up width 1200   # error: cross extent is 900
```

## E_STRIP_NEST

*error* — Illegal `strip` nesting.

**Cause.** A `strip` block appears inside a component, a control-flow block, or another `strip`. Strips place rooms and are only legal as direct plan-level statements.

**Fix.** Move the `strip` to the plan body, alongside the other elements.

```arch static
component c() { strip right at (0,0) gap 0 { … } }   # error: nested strip
```

## E_STRIP_SIZE

*error* — Room in a `strip` is missing a size.

**Cause.** A room inside a `strip { … }` gives no main-axis extent, or gives none while the strip supplies no cross-axis `height`/`width` for it to inherit — so its rectangle is undetermined.

**Fix.** Give the room a `size <main>` (main-axis extent) plus either a strip `height`/`width` or its own `size <main>x<cross>`.

```arch static
strip right at (0,0) gap 100 { room size }   # error: no extent
```

## E_TYPE

*error* — Type mismatch.

**Cause.** A value was used where another type was required (e.g. a string where a number is expected, or a non-array in `for`).

**Fix.** Convert or supply the expected type.

```arch static
room at (0,0) size "big" x 10   # error: size needs numbers
```

## E_UNKNOWN_COMPONENT

*error* — Unknown component.

**Cause.** An instance calls a component name that is not defined or imported.

**Fix.** Define the component, import it, or fix the name (see the suggestion hint).

```arch static
sofa(0, 0)   # error if no `component sofa` is in scope
```

## E_UNKNOWN_FN

*error* — Unknown function.

**Cause.** A call uses a name that is neither a built-in nor a value-function in scope.

**Fix.** Define it with `let f(…) = …`, or fix the name.

```arch static
let x = frobnicate(2)   # error
```

## E_UNKNOWN_REF

*error* — Unknown reference.

**Cause.** An expression references a name that is not bound in scope.

**Fix.** Declare it with `let`, pass it as a parameter, or fix the typo.

```arch static
let x = y + 1   # error if `y` is undefined
```

## E_VERT_SIZE

*error* — Vertical circulation must have a positive size.

**Cause.** A `stair`, `elevator` or `escalator` footprint's width or height evaluated to zero or a negative number, so there is no run to draw.

**Fix.** Give it a positive `size W x H` — the footprint the run occupies on this storey.

```arch static
stair id=s at (0,0) size 900x0 dir up   # error: zero depth
```

## E_VOID_SIZE

*error* — A floor void must have a positive size.

**Cause.** A `void`'s width or height evaluated to zero or a negative number, so there is no opening in the floor plate to draw or to route circulation around.

**Fix.** Give it a positive `size W x H` — the hole's extent in plan.

```arch static
void id=well at (2000,1000) size 0x3000   # error: zero width
```

## E_WALL_THICKNESS

*error* — Wall must have a positive thickness.

**Cause.** A wall's `thickness` evaluated to zero or a negative number.

**Fix.** Give the wall a positive `thickness`.

```arch static
wall exterior thickness 0 { (0,0) (1,0) }   # error
```

## E_WHILE_LIMIT

*error* — `while` exceeded its iteration cap.

**Cause.** A `while` ran more times than the safety cap allows (usually a condition that never becomes false).

**Fix.** Ensure the loop body updates a binding so the condition eventually fails.

```arch static
let i = 0
while i < 1 { column at (0,0) size 1x1 }   # error: i never changes
```

## E_WINDOW_WIDTH

*error* — Window must have a positive width.

**Cause.** A window's width evaluated to zero or a negative number.

**Fix.** Give the window a positive `width`.

```arch static
window at (0,0) width 0   # error
```

## W_ALIAS_MATCH

*warning* — A room's use was inferred from an indirect alias, not stated.

**Cause.** A room has no authored `uses`, and its function was guessed from its label via a non-canonical alias (an indirect term like `powder` → WC, `foyer` → entry) rather than a direct word (`wc`, `entrance`). The guess is reasonable but the intent is implicit, so a reader (or agent) cannot tell the classification was assumed.

**Fix.** Add an explicit `uses …` to the room stating the inferred function — the machine-applicable fix inserts it for you. This pins the classification without changing the room's `describe()` type.

```arch static
room at (0,0) size 2000x1500 label "Powder"   # warning: WC inferred from the alias "powder"; add `uses wc`
```

## W_BALCONY_NO_DOOR

*warning* — A balcony with no way onto it.

**Cause.** An `outdoor balcony` has no `door` and no `window` within one wall thickness of any of its edges, so the drawing shows a slab that cannot be reached. A window counts as well as a door, because a full-height window is a normal way onto a balcony.

**Fix.** Add a door (or a full-height window) on the wall the balcony hangs off — the usual fix — or move the balcony to the facade that already has one.

```arch static
outdoor balcony at (2000,7200) size 4000x1600   # warning: no opening within reach
```

## W_BATH_VIA_BEDROOM

*warning* — Bathroom is reachable only through a bedroom.

**Cause.** Every door path from the entrance to this bathroom/WC passes through a bedroom. That is fine for a private en-suite, but a dwelling's main bathroom should open off circulation (a hall or living space), not a bedroom.

**Fix.** Add a door connecting the bathroom to a hall/living space, or route circulation so it is not reached only via a bedroom.

```arch static
door id=d_bath at (5200,4000) width 800 wall partition   # lint: bath only off the bedroom
```

## W_BEDROOM_NO_WINDOW

*warning* — Bedroom has no window.

**Cause.** A room labelled as a bedroom has no window on its perimeter (natural light / egress).

**Fix.** Add a `window` on an exterior wall of the room.

```arch static
room at (0,0) size 3000x4000 label "Bedroom"   # lint: no window
```

## W_CIRCUITOUS_PATH

*warning* — A room is reached by a very roundabout path.

**Cause.** The walking distance from the entrance to a room is many times its straight-line distance — the room is reachable but only by a circuitous route (e.g. all the way around the plan). A coarse circulation fact (ADR 0008); the default ratio is generous so a normal tucked-away room does not trip it.

**Fix.** Add a more direct connection — a door or a hall — so the room is not reached the long way round.

```arch static
room id=bed at (0,0) size 3000x3000 label "Bed"   # only door is on the far side, forcing a long detour
```

## W_DIM_INSIDE

*warning* — A hand-written dimension line lands inside the building.

**Cause.** A `dim`'s line is drawn at `offset` along the LEFT normal of from→to, so the ENDPOINT ORDER chooses which side it lands on. This dimension's line (not its witness lines) falls inside the room-extents bounding box, where it crosses room labels, furniture and poché instead of reading in the page margin. Almost always the endpoints are simply the wrong way round. Advisory: an interior dimension you actually want (a clear width inside a room) is legitimate — reverse it or leave the warning.

**Fix.** Swap the two endpoints — the machine-applicable fix does it for you, but only when the swap actually moves the line out. Swapping mirrors the line across the segment being measured (negating the `offset` does the same thing), so it reaches the outside only when that segment is at the building's edge. A dimension whose measured run cuts THROUGH the plan reads inside either way and carries no automatic fix: measure along a facade instead, or raise the `offset` until the line clears the building.

```arch static
dim (0,6000)->(7000,6000) offset -500 text "7000"   # warning: the -500 offset pulls the line back inside
```

## W_DIM_NO_WALL

*warning* — A `dim faces`/`dim clear` endpoint has no wall to measure to.

**Cause.** The endpoint-projection forms push each endpoint onto the FACE of the wall the measurement runs into, found as the nearest wall segment perpendicular to the dimension's own direction. One (or both) endpoints has no such wall within tolerance — the point is off the wall centerline, the wall is angled, or the plan has no wall there — so there is no face to project onto and the written point is measured as-is.

**Fix.** Put the endpoint on the centerline of the wall the dimension runs into (the room-rectangle corner coordinate), or drop the `faces`/`clear` keyword and write the face coordinate yourself.

```arch static
wall exterior thickness 200 { (0,0) (5000,0) (5000,4000) (0,4000) close }
dim faces (0,9000)->(5000,9000) offset 600   # warning: nothing to project onto at y9000
```

## W_DIM_OVERLAP

*warning* — Two hand-written dimensions are drawn on top of each other.

**Cause.** Two `dim` statements measuring PARALLEL runs land in the same chain tier: their drawn bands — the dimension line, its station ticks, and the length text riding above the line — intersect, so on paper one reads through the other. ArchLang never re-staggers a hand-written dimension (the author's `offset` IS the tier control — ADR 0005, no invisible architect), so the collision is reported rather than silently laid out. Adjacent members of one chain that merely SHARE a station tick are not a collision and never warn, and two dims generated by one `for` are skipped — they share an `offset` expression, so no bump could separate them.

**Fix.** Move one of them out a tier: raise the magnitude of its `offset` (keeping its sign, which is the side it reads on). The machine-applicable fix computes the smallest whole number of chain tiers that clears the other dimension's line and text.

```arch static
dim (0,-100)->(7000,-100) offset -300 text "7000"
dim (0,-100)->(4000,-100) offset -300 text "4000"   # warning: both in the same tier
```

## W_DOOR_CLEARANCE

*warning* — Door is narrower than the minimum clear width.

**Cause.** A door's width is below the configured minimum passable width (default 700 mm).

**Fix.** Widen the door to at least the minimum clear width.

```arch static
door at (0,0) width 500 wall exterior   # lint: under 700 mm
```

## W_DOOR_NEAR_CORNER

*warning* — A door leaves less wall between its jamb and a corner than the wall is thick.

**Cause.** The nib of wall between a door's jamb and the corner its host wall turns at is shorter, along the run, than the wall is thick across it. **The drawing is correct** — the nib IS drawn, and mitred into the neighbouring run — so this is a fact about the plan, not a rendering fault; it was first mis-diagnosed as one during the v1.30 joinery review before it was measured. At page scale such a sliver stops reading as wall continuing to the corner and reads as a chamfer on the corner, and its returned face has nowhere to carry the frame and architrave a jamb is fixed to. The warning states the nib required, the nib measured, and the shortfall. **The threshold is the wall's own thickness** (× `minCornerNibRatio` in the lint ruleset, default 1.0): it is the only length in the drawing intrinsic to the wall being measured, so the rule needs no new absolute constant and scales by itself — a 100 mm partition asks for 100 mm, a 400 mm shell for 400 — and it is the dimension the defect is about. Unlike `W_POCKET_RUN` the threshold is one-limbed, because both sides of the comparison belong to the same wall and there is no narrow-door pathology to guard against. **What counts as a corner:** the host segment's run must END at that point and the wall must go somewhere else from it, so a wall's free end, a redundant collinear vertex, a partition teeing into a wall that carries straight past it, and a TANGENT arc/straight hand-over are all excluded — nothing is mitred at any of them. **On a curve the nib is an arc length, not a chord**, measured at the wall's centreline radius from the radial jambs, in closed form (nothing tessellates, so the number cannot move with a facet count). Kind-independent: a jamb is a jamb whether the leaf swings, slides, folds or parks overhead. Scoped to doors deliberately — a window nib is a different detail with no frame to hang.

**Fix.** Move the door further from the corner by the shortfall the warning quotes (`on <wall> at <pos>` measures along the run, so the position is the one number to change), or lengthen the wall past the door so the corner moves away from the jamb instead. There is no machine-applicable fix: every remedy rewrites a number the author chose. Narrowing the leaf would also close the gap and is deliberately NOT offered — rewriting the width you asked for to satisfy a checker is the constraint-laundering pattern this project rules out, and it heads toward `W_DOOR_CLEARANCE`.

```arch static
wall w1 thickness 250 { (0,0) (5000,0) (5000,4000) }
door on w1 at 4400 width 900   # lint: a 150 mm nib on a 250 mm wall
```

## W_DOOR_OFF_WALL

*warning* — Door does not lie on any wall.

**Cause.** A door's position is not within tolerance of any wall segment, so it has no host.

**Fix.** Move the door onto a wall, or name its host with `wall <id|category>`. The diagnostic points at the nearest wall.

```arch static
door at (9999,9999) width 900   # warning: not on a wall
```

## W_DOORWAY_BLOCKED

*warning* — A doorway's landing is blocked.

**Cause.** A piece of furniture/fixture sits in the clear landing space immediately on either side of a door opening, so you cannot pass through the doorway even when the leaf is open. This is the approach path, distinct from the leaf's swing arc (`W_SWING_OBSTRUCTED`). The warning states the landing depth required, the depth actually left, and the shortfall.

**Fix.** Move the obstruction clear of the opening by the shortfall the warning quotes (`arch repair` computes the smallest clearing shift), shrink it by that much on the axis facing the door, or move the door along its wall so its landing misses it.

```arch static
door at (6000,3000) width 800
furniture wc at (5800,3050) size 700x400   # lint: WC blocks the doorway
```

## W_DRAWING_OVERFLOW

*warning* — The whole drawing does not fit the declared paper, even though the building does.

**Cause.** Same ruler as `W_SCALE_OVERFLOW`, different subject. That rule measures the BUILDING's outer-face extent against the sheet minus its margins, dimension bands, title block and margin tables. This one measures **everything the plan draws** against that same area — and a plan draws more than its building: `outdoor` ground, a `fence`, a `site … boundary`, a `roof` eaves line, a fixture set down outside the shell. None of that is in the building's extent, so none of it can make the fit test say no, and `sheet.fits` stays `true` while the sheet cannot hold the drawing. The two are mutually exclusive by design: the drawn extent CONTAINS the building, so when the building itself overflows you get `W_SCALE_OVERFLOW` instead and this would only repeat it. The warning quotes the whole drawing's extent, the drawing area it is measured against, and the overflow on each axis — a millimetre of eaves and a 40 m yard want different answers. **It does not claim the page grows:** nothing is clipped, the reserved sheet margin gives way first, and only a large overrun pushes the page past the paper. `describe().sheet.drawing_fits` is this fact; `scene.sheet.page` is the page that actually came out. Advisory: a site plan you intend to issue oversize, trim or tile is legitimate.

**Fix.** Move up a paper size or pick a coarser scale — both also shrink the building, so check the drawing still reads — or draw less ground. There is deliberately NO machine fix: every remedy rewrites a decision the author made, and unlike a building overflow there is no defensible default, since re-scaling shrinks a building that was already sized correctly for the sheet (ADR 0005).

```arch static
paper A4 portrait
scale 1:100
outdoor lawn at (0,3500) size 4000x40000 label "Yard"   # warning on a 4 m cottage: the yard is 40 m long
```

## W_DUP_ACC_METADATA

*warning* — Duplicate `accTitle`/`accDescr`.

**Cause.** A plan declares `accTitle` (or `accDescr`) more than once. Only one of each applies, so the last value silently wins.

**Fix.** Keep a single `accTitle` and a single `accDescr`; delete the extra line(s).

```arch static
accTitle "A"
accTitle "B"   # warning: "A" is discarded
```

## W_EMPTY_PLAN

*warning* — Empty plan.

**Cause.** The plan resolved to no drawable elements.

**Fix.** Add at least one element (wall, room, …).

```arch static
plan "Empty" { units mm }   # warning
```

## W_FIXTURE_BACK_TO_ROOM

*warning* — A fixture stands against a wall but faces the wrong way.

**Cause.** A fixture whose symbol has a distinct back — a plumbed one (a WC's cistern, a basin's tap, a counter's nosing) or a piece of furniture that simply has a front (a bed's headboard, a wardrobe's door line, a bookshelf's open face) — is touching a wall, but its back is turned to the room instead of to that wall: the drawing shows the WC's tank, or the headboard, sticking into the floor space. Position right, quarter-turn wrong: fixture symbols are drawn with their back on the TOP edge, so `rotate` is what aims them (0 = back to the north, 90 = east, 180 = south, 270 = west). A rotation-symmetric symbol (a shower tray, a round stool) never trips this, nor does arranged seating whose back to the room is a legitimate layout. A fixture that touches no wall at all is `W_FIXTURE_FLOATING` instead — and only where the piece genuinely needs one for services, which furniture does not.

**Fix.** Add the `rotate` that puts the back on the walled edge — the machine-applicable fix inserts it when exactly one edge is walled. Better still, place the piece with `against wall <id>` or `in <room> anchor <edge>` and let the rotation be derived.

```arch static
furniture wc at (5200,5200) size 400x700   # warning: south wall behind it, cistern facing north
```

## W_FIXTURE_FLOATING

*warning* — A plumbing/kitchen fixture is not against a wall.

**Cause.** A fixture that conventionally needs a wall behind it (WC, basin, shower, sink, counter, stove, fridge…) sits with no wall backing any edge — it appears to float in the middle of the room.

**Fix.** Move the fixture so one edge is against a wall (supply/waste/venting runs in the wall), or remove it.

```arch static
furniture wc at (3000,3000) size 400x700   # lint: no wall behind it
```

## W_FIXTURE_WRONG_ROOM

*warning* — Fixture is not inside its declared room.

**Cause.** A furniture item declared `in <roomId>` has part of its FOOTPRINT off that room's floor, so it is drawn partly (or mostly) in the wrong space. A 100 mm overhang is allowed on every side, because a room's outline runs down wall centrelines and a piece pushed against a room edge legitimately shares that band. Measured against the room's true floor, so a `polygon` or `circle` room is judged by its own shape and not by a bounding box.

**Fix.** Move the fixture inside the named room, or correct the `in <roomId>`.

```arch static
furniture wc at (100,100) size 400x700 in bath   # lint: not inside "bath"
```

## W_FURN_CLEARANCE

*warning* — A fixture's use-space is blocked.

**Cause.** The activity clearance directly in front of a fixture (WC, basin, sink, counter, stove…) is intruded by a free-standing piece of furniture, so the fixture can't be used comfortably. Other plumbing/kitchen fixtures are ignored, so a compact bathroom/kitchen run does not trip this. The warning states the catalogued clearance, the depth actually left, and the shortfall.

**Fix.** Move or shrink the obstructing furniture by the shortfall the warning quotes, turn the fixture so its front faces clear floor (its back must stay on a wall), or move the fixture to a wall run with the clearance free in front of it.

```arch static
furniture stove at (0,0) size 600x600
furniture sofa at (0,650) size 2000x900   # lint: sofa blocks the stove front
```

## W_FURNITURE_OVERLAP

*warning* — Two pieces of furniture overlap.

**Cause.** Two furniture/fixture rectangles occupy the same floor area, so they would physically collide — usually a coordinate or size mistake.

**Fix.** Move or resize one so they no longer intersect; leave a walkway between them.

```arch static
furniture sofa at (300,300) size 2000x900
furniture bed  at (1000,500) size 1500x2000   # lint: overlaps the sofa
```

## W_FURNITURE_WALL_COLLISION

*warning* — Furniture penetrates a wall.

**Cause.** A furniture/fixture rectangle intrudes into a wall's solid (it crosses the wall's thickness band rather than sitting flush against its face), so it would physically pass through the wall — a coordinate or size mistake. A piece merely touching the wall face is fine. Measured in the wall's own frame, so a wall at any angle is checked; a CURVED wall edge (`arc`) is not checked at all.

**Fix.** Move or resize the piece so it sits fully inside the room (against the wall face, not through it), or anchor it with `against wall <id>`.

```arch static
furniture sofa at (350,2300) size 2000x900   # lint: crosses the partition at y3000
```

## W_GARAGE_TOO_NARROW

*warning* — A garage is too narrow to park in.

**Cause.** A room classified as a garage (an authored `uses garage`, or a label the use vocabulary reads as one — "Garage", or the aliases "carport" / "parking") measures less across its short side than the cars in it need: 2700 mm of clear width per bay, which is a body plus the room to open a door on each side. A garage with no `car` fixture drawn is measured against ONE bay — a room you could not park in does not become sound by having no car in it — and `bicycle`/`motorcycle` do not count toward the bay total. The warning states the width required, the width the room actually has, and the shortfall. **The rule declines a POLYGON room** rather than measuring its bounding box: a box's short side is not a concave floor's clear width, and a measurement taken from a shape's box instead of the shape is a defect class this compiler has shipped before. It is advisory and has no machine fix: widening a room moves every wall and opening around it, which is a geometric decision the compiler does not make (ADR 0005).

**Fix.** Widen the room to the figure the warning quotes, park fewer cars in it, or drop the `uses garage` tag if the room is not one (a store that happens to be called a garage classifies as one from its label alone — an explicit `uses storage` overrides that).

```arch static
room id=g at (0,0) size 2500x6000 label "Garage" uses garage
furniture car at (300,300) size 1800x4600 in g   # lint: 2500 mm across, one bay wants 2700
```

## W_HATCH_SCALE

*warning* — Hatch scale must be positive; using 1.

**Cause.** A wall material `scale` evaluated to zero or a negative number.

**Fix.** Use a positive `scale`.

```arch static
wall exterior thickness 200 material brick scale 0 { (0,0) (1,0) }
```

## W_IMPORT_EMPTY_FILE

*warning* — Whole-file import binds an empty component.

**Cause.** `import "<file>" as <name>` turns a module's TOP-LEVEL drawable statements into one component, but that module's plan body has none (it may only declare `component`s, or only `level` blocks — a storey is a page, so levels are dropped).

**Fix.** Draw in the module's plan body, or import one of its named components with `import "<file>": <name>`.

```arch static
import "lib.arch" as lib   # warning when lib.arch only declares components
```

## W_NO_ENTRANCE

*warning* — The plan has no exterior door.

**Cause.** The plan has rooms and an exterior wall but no door hosted on an exterior wall, so the building cannot be entered.

**Fix.** Add a `door` on an `exterior` wall.

```arch static
wall exterior thickness 200 { (0,0) (4000,0) (4000,3000) (0,3000) close }   # lint: no way in
```

## W_OPENING_OFF_WALL

*warning* — Opening does not lie on any wall.

**Cause.** A cased opening's position is not within tolerance of any wall segment, so it has no host.

**Fix.** Move the opening onto a wall, or name its host with `wall <id|category>`. The diagnostic points at the nearest wall.

```arch static
opening at (9999,9999) width 1000   # warning: not on a wall
```

## W_OUTDOOR_OVERLAPS_ROOM

*warning* — A ground surface is laid over a room's floor.

**Cause.** An `outdoor` surface's extent overlaps the floor of a room by more than a shared edge. Almost always a coordinate slip — a terrace written from the wrong corner, or a lawn given the building's own extent. It matters because ground and floor are reported in SEPARATE totals (`totals.outdoor_area_m2` and `totals.floor_area_m2`), so the overlapping piece is counted twice by anything that adds them for a plot figure. A surface that merely touches a room along its edge — a patio flush against the house — does NOT trip this.

**Fix.** Move or resize the surface so it sits outside the building's rooms. If the overlap is deliberate (a covered terrace drawn under a room), the two are genuinely different things and one of them is the wrong element.

```arch static
room id=liv at (0,0) size 5000x4000 label "Living"
outdoor paving at (1000,1000) size 3000x2000   # warning: on the living-room floor
```

## W_PATH_TOO_NARROW

*warning* — The walk to a room squeezes below a passable width.

**Cause.** The widest route from the entrance into a room (or between a key pair of rooms, e.g. bedroom→bath) has an unavoidable pinch narrower than the minimum passable clear width — a too-narrow door/opening, or furniture crowding the way. The limit case is reported too: when no route reaches the room at all, the message gives the MEASURED width of the best way in that does exist (found by asking which smaller body does get there) rather than a fabricated zero, and calls the walk blocked only when there is no gap at all. A coarse fact from the circulation nav grid (ADR 0008); the number is grid-quantised, and it is the width a body passes through, not the room left over after its own clearance.

**Fix.** Widen the tightest door/opening on the route to at least the minimum, move the furniture pinching it, or add a second way in so the pinch is avoidable. There is no machine-applicable fix: the bottleneck is a nav-grid cell, not a named element, so nothing can be rewritten for you.

```arch static
door at (4000,1500) width 600
furniture cabinet at (3600,300) size 700x1200   # lint: the way through squeezes below 700 mm
```

## W_POCKET_RUN

*warning* — A pocket door has no wall to slide into.

**Cause.** A `pocket` door's panel disappears into a cavity inside the host wall, so the wall must continue past the slide-side jamb by at least the door's own width plus end clearance. This measures the run from that jamb to the end of the host segment, TRUNCATED at the near edge of any other opening (a window or a second door) that falls inside it — a panel cannot slide through a window either. The warning states what the run has to be, what is actually there, and the shortfall. **The threshold, and a deliberate divergence from the source it is borrowed from:** the required run is `width + max(50 mm, width × 5%)` (`pocketRunClearanceMm` in the lint ruleset). The rule is modelled on planscript-rust's `pocket_door_wall_run`, which requires a flat `width × 1.05` — but a pure ratio is wrong on narrow doors: a 700 mm pocket would ask for 35 mm of end clearance, which does not fit a real jamb and pull. We are not publishing a comparison against that project, so matching its constant buys nothing, and architectural correctness outranks reference-comparability. The two extra things this rule does that the reference does not: it subtracts intervening openings, and it carries its remedies as structured fixes rather than printed prose.

**Fix.** Reverse the slide (`slide left` ↔ `slide right`) — a machine-applicable fix, emitted only after the reverse run is recomputed and proved to satisfy; move the door along its wall so the pocket lands on solid wall; or lengthen the wall. Narrowing the door is deliberately NOT offered as a fix: rewriting the author's stated width to satisfy a checker is the constraint-laundering pattern this project rules out, and it would also walk into `W_DOOR_CLEARANCE`.

```arch static
wall w1 thickness 200 { (0,0) (3000,0) }
door pocket on w1 at 80% width 900 slide right   # lint: only ~600 mm of run
```

## W_ROOM_DISCONNECTED

*warning* — Room has no door — it can't be entered.

**Cause.** No door lies on any of the room's walls, so there is no way into the room.

**Fix.** Add a `door` on one of the room's walls.

```arch static
room id=r at (0,0) size 3000x3000   # lint: no door on its perimeter
```

## W_ROOM_LABEL_OUTSIDE

*warning* — A room's explicit label anchor falls outside the room.

**Cause.** `label "…" at (x,y)` pins the label and area text at a point that is not inside the room's floor, so the text will be drawn over whatever is there instead.

**Fix.** Move the anchor inside the room, or drop the `at (…)` — automatic placement uses the rectangle's centre, the circle's centre, or a polygon's area centroid (falling back to the interior point furthest from any edge where a concave ring puts that centroid off its own floor), so it lands inside either way.

```arch static
room polygon (0,0) (6000,0) (6000,6000) (0,6000) label "Hall" at (9000,9000)   # warning
```

## W_ROOM_NO_CLEAR_PATH

*warning* — A room cannot be entered or crossed.

**Cause.** Furniture, fixtures, door swings and their clearances fill the room so densely that a person stepping through a door/opening has no clear floor path into the usable space — the room is technically reachable but physically blocked.

**Fix.** Open up the layout: move or shrink the furniture nearest the door so there is a continuous walkable strip from each entrance into the room.

```arch static
furniture shower at (5000,3000) size 2000x2000   # lint: fills the bathroom against its only door
```

## W_ROOM_NO_FIXTURE

*warning* — Bathroom or kitchen has no fixtures.

**Cause.** A room labelled as a bathroom or kitchen contains no plumbing/kitchen fixture (WC, basin, shower, sink, counter…), so it is drawn as an empty box.

**Fix.** Place the expected fixtures — e.g. import `lib/fixtures.arch` and add a `wc`, `basin`, `shower`, or `kitchen_sink`.

```arch static
room at (4000,4000) size 3000x2000 label "Bath"   # lint: no fixtures inside
```

## W_ROOM_NOT_ENCLOSED

*warning* — Bathroom is not fully enclosed.

**Cause.** A run of this bathroom/WC's perimeter is not backed by a wall, so it is open to the adjacent space — a privacy problem for a wet room (a partition that stops short is the usual cause).

**Fix.** Extend the partition so the room's perimeter is walled on all sides (a door/window in the wall is fine — only a missing wall counts).

```arch static
wall partition thickness 100 { (4000,0) (4000,4000) }   # lint: stops short, bath left open
```

## W_ROOM_NOT_EQUATOR_FACING

*warning* — A habitable room has windows, but none faces the equator side.

**Cause.** The plan declares `site`, so the equator-facing side is known (`S` in the northern hemisphere, `N` in the southern), and a habitable room — a bedroom, living or dining space — has at least one window with none of them on that aspect. **This is a drafting heuristic, not a measured daylight outcome:** it says the room's windows do not face the equator, and nothing more. A south window in Reykjavík and one in Singapore are not the same daylight, and this rule does not distinguish them (there is no sun model, latitude or date anywhere in ArchLang). The rule cannot fire at all in a plan with no `site`, and a room with NO window is `W_BEDROOM_NO_WINDOW`'s report, not this one.

**Fix.** Move a window onto the room's equator-facing facade, or accept the aspect — an urban plot often has no equator-facing wall to spare. There is deliberately NO machine fix: the remedy is a geometric decision (which facade, which wall, what else it displaces) and the compiler does not make those (ADR 0005).

```arch static
site { street north }   # equator side is S
room id=liv at (0,0) size 4000x3000 label "Living"
window on north_wall at 50% width 1200   # warning: the only window faces N
```

## W_ROOM_OVERLAP

*warning* — Rooms overlap.

**Cause.** Two room rectangles intersect.

**Fix.** Adjust positions/sizes if the overlap is unintended (it is allowed).

```arch static
room at (0,0) size 2000x2000
room at (1000,0) size 2000x2000   # warning
```

## W_ROOM_TOO_SMALL

*warning* — Room is implausibly small.

**Cause.** A room's floor area is below the configured minimum (default 4 m²).

**Fix.** Increase its `size`, or merge it into an adjacent space.

```arch static
room at (0,0) size 1000x1000 label "Closet"   # lint: 1 m²
```

## W_ROOM_UNREACHABLE

*warning* — Room cannot be reached from the entrance.

**Cause.** The building has an entrance, but this room has no door/opening path back to the exterior — it is sealed off from the circulation.

**Fix.** Add a door or cased `opening` linking it (directly or through a hall) to a space that reaches the entrance.

```arch static
room at (5000,0) size 3000x3000 label "Store"   # lint: no path from the entrance
```

## W_SANITIZED_CONFIG

*warning* — A disallowed config value was stripped.

**Cause.** A theme/style value contained markup or a `data:` URL and was blanked for safety.

**Fix.** Use a plain colour/string value (no `<`, `>`, or `url(data:…)`).

```arch static
theme { wall: "<script>" }   # warning: stripped
```

## W_SCALE_OVERFLOW

*warning* — The drawing does not fit the declared paper at the declared scale.

**Cause.** The plan declares a `paper` size, so `scale` is operative: the building's outer-face extent has to fit the sheet minus its margins, the `dims auto` dimension bands, the bottom chrome band (scale bar + title block) and the margin-table row that `schedule rooms` / `legend` occupy below it. At the scale in force it does not. Your scale is NEVER silently overridden — a drawing is issued at the scale printed in its own title block — so nothing is clipped: the drawing keeps its sheet margin where it can, and the page grows past the sheet when it cannot. Note that the SHEET FURNITURE counts, not just the building: adding a schedule to a tight sheet can raise this with no change to the plan's geometry. Advisory: an oversize sheet you intend to trim or tile is legitimate.

**Fix.** Pick a coarser scale (a larger denominator draws the building smaller), move up a paper size, drop a margin table you can do without, or drop the `scale` line and let the sheet auto-fit choose the finest scale from 1:50 / 1:100 / 1:200 / 1:500.

```arch static
paper A4 landscape
scale 1:50   # warning on a 20 m building: 1:100 or `paper A2` fits, or omit `scale` to auto-fit
```

## W_STAIR_UNMATCHED

*warning* — A run of vertical circulation appears on only one storey.

**Cause.** In a multi-storey plan the ONLY thing that joins two floors is identity: a `stair`/`elevator`/`escalator` with the same `id` in two `level` blocks is one shaft. This id appears on exactly one storey, so it connects nothing — usually a typo in the id, or the matching run was never drawn on the floor above or below. **Known limitation:** a top-floor stair that genuinely goes only to a roof hatch, and a lift that stops short of a storey it passes, both trip this rule; it is advisory, so leave it.

**Fix.** Draw the same run with the SAME `id` on the neighbouring storey (each storey declares its own `dir` — `up` on the lower floor, `down` on the upper one), or fix the id.

```arch static
level 1 "G" { stair id=s at (0,0) size 900x2600 dir up }
level 2 "1" { }   # warning: "s" is on level 1 only
```

## W_SWING_OBSTRUCTED

*warning* — Door swing is obstructed.

**Cause.** The quarter-circle a door leaf sweeps overlaps a piece of furniture/fixture or another door's swing, so the door cannot open fully. The warning states the clear radius the swing needs, what it actually has, and the shortfall.

**Fix.** Hang the leaf on the other jamb (`hinge left|right`) — a machine-applicable fix when the flipped swing is proved clear; or open it the other way (`swing in|out`), move the door along its wall, move the obstruction (`arch repair`), narrow the leaf to the width the warning quotes (never below the minimum passable width — that relocates the problem into `W_DOOR_CLEARANCE`), hang no swinging leaf at all (a `sliding`, `pocket` or `barn` door sweeps nothing, so this rule cannot apply to it), or make it a leafless `opening`.

```arch static
door at (4000,1500) width 900 swing in   # lint: leaf sweeps onto the bed
```

## W_SWING_ROOM_NOT_ADJACENT

*warning* — `swing into <room>` names a room the door does not border.

**Cause.** A door's `swing into <room>` points the leaf toward a room, but that room does not share the door's host wall (its box does not touch the wall within tolerance), so which side it means is undefined.

**Fix.** Point `swing into` at a room the door actually opens onto, or use explicit `swing in|out`. The door falls back to its default swing.

```arch static
door on w1 at 50% width 800 swing into faraway   # warning: not on this wall
```

## W_UNKNOWN_MATERIAL

*warning* — Unknown wall material; using the default hatch.

**Cause.** A wall `material` name is not one of the known hatches.

**Fix.** Use a known material (e.g. brick, concrete, insulation, tile) or omit it.

```arch static
wall exterior thickness 200 material marble { (0,0) (1,0) }   # warning
```

## W_UNKNOWN_STYLE_KEY

*warning* — Unknown style key.

**Cause.** A `style <kind> { … }` block uses a key not valid for that element kind.

**Fix.** Use a valid key (e.g. fill / stroke / label, depending on the kind).

```arch static
style room { nope: "#000" }   # warning
```

## W_UNKNOWN_THEME_KEY

*warning* — Unknown theme key.

**Cause.** A `theme { … }` block uses a key that is not a theme property or alias.

**Fix.** Use a known theme key (see the language reference / hover).

```arch static
theme { nope: "#000" }   # warning
```

## W_WINDOW_OFF_WALL

*warning* — Window does not lie on any wall.

**Cause.** A window's position is not within tolerance of any wall segment, so it has no host.

**Fix.** Move the window onto a wall, or name its host with `wall <id|category>`. The diagnostic points at the nearest wall.

```arch static
window at (9999,9999) width 1200   # warning: not on a wall
```
