Skip to content

rulec diagnostics

Every code rulec can print, what makes it appear, and how to fix it. The code and the JSON shape are a stable API; only the prose improves. For one of them: rulec explain E101.

Code Severity Title
E001 error Unterminated string
E002 error Unreadable character
E003 error The file does not start with a rule line
E004 error The line does not start with a word
E005 error A word that cannot appear at this position
E006 error The declaration has no =
E007 error No such policy
E008 error Empty cell
E009 error A declared name collides with a reserved word
E010 error The .. range notation is not allowed
E011 error A public name has no ASCII alias
E012 error The name is not declared
E013 error No such import
E014 error An output cell cannot hold an expression
E015 error result can only assemble the first output
E016 error There can be only one result
E017 error A constraint is not shaped like this
E018 error A constraint relates two inputs
E019 error An example is an input the generated code refuses at its door
E020 error The elements declaration is not right
E021 error The fold is not written correctly
E022 error The answer for a sequence with no elements is not declared
E023 error The answer for a walk that reached the end is not declared
E024 error Some verdict has no arm
E025 error The examples have no column for the sequence
E026 error The sequence is not written correctly
E027 error The example names a sequence that is not there
E028 error The count is not written correctly
E029 error This column cannot be counted
E030 error A count needs a range
E031 error One item reads the elements of two sequences
E032 error The declared enum and the imported one disagree
E033 error A value that came from elsewhere has neither a row nor default
E034 error A name appears twice
E035 error The target of overrides does not exist
E036 error The target of overrides does not define the same output
E037 error A cited fragment is not pinned
E038 error A source fragment has changed
E039 error There is no copy of a source
E040 error A file relied on differs from its pin
E041 error The bindings of an apply do not match the callee
E042 error A binding does not agree in type
E043 error A value passed leaves the callee's range or constraint
E044 error That rule cannot be applied
E045 error A table that shares an output has two or more output columns
E046 error A clause is not shaped like this
E047 error Extra token after the declaration
E049 error A thousands separator cannot be written
E048 error This type has no arithmetic
E050 error The machine section is not shaped right
E051 error carry or over does not fit the declarations
E052 error A state the machine names does not fit
E053 error The once line does not fit the declarations
E054 error A rule that folds a sequence cannot be the step of a machine
E055 error A scenario is not shaped right
E056 error The held line does not fit the declarations
E057 error The declaration has no type
E058 error A declaration line cannot be read
E059 error The expression cannot be read
E060 error A step is not positive
E061 error The range is empty
E062 error There is no such date
E063 error The cell cannot be read
E064 error The row does not fit the header
E065 error range from koyomi is misshapen or misplaced
E066 error An enums file, or an import enum from one, is misshapen
E069 error An ordered enum, a comparison on one, or a display name is misshapen
E071 error The prerequisites are misshapen
E072 error A carried value is not written right
E075 error A word is both a value of an enum and a name
E101 error Completeness gap: some input matches no row
E102 error Unreachable row: the row never matches
E103 error Unit mismatch: values of different types are being mixed
E104 error A numeric output declares no rounding
E105 error Overlapping rows: the same input matches two or more rows
E106 error An output literal is not on the rounding grid
E107 error An example does not match
E108 error Cannot prove an intermediate value fits in int64
E109 error The check exceeded its budget, so completeness could not be proven
E110 error A column has a type the check cannot handle
E111 error The examples have no column for an output
E112 error The range of a derived value does not contain the values it can reach
E113 error The condition of a boolean definition is neither of the two allowed forms
E114 error A value does not sit on the declared step
E115 error The divisor is not a positive constant
E116 error A row's amount is not in the copy it cites, or is under another heading there
E117 error A share without what a share needs
E118 error The call is not written correctly
E120 error A from does not fit the input's type
E121 error The contract has no such path
E119 error A row's boundary falls on the other side from the copy it cites
W122 warning No input is projected from that shape
E122 error The contract lets through a value the rule refuses
W123 warning A row is reached only by values the contract does not let through
E123 error The contract lets through a combination the rule's constraint refuses
W124 warning A row is reached only by a combination the contract does not let through
E124 error A final state has a way out
E125 error A case can reach a state it can never finish from
E126 error A never line is broken
E127 error A once line is broken
E128 error The machine's claims could not be checked
E129 error This rulec reads no koyomi file
E130 error The days of the koyomi date cannot be read
E132 error The divisor can be zero
E139 error The argument of isqrt can be below zero
E140 error An output's range does not hold the values it takes
E134 error A call answers a carried number outside its range
E135 error An always line is broken
E136 error The prerequisites go round in a cycle
E137 error A value cannot be reached from start
E138 error A prerequisite comes after the value in the order of ordered by
W125 warning No sequence of calls reaches a state
W126 warning A transition is never taken from a state a case can reach
W127 warning A claim of the machine could not be settled
W128 warning No input was found that reaches a row, and none was shown not to
W130 warning A prerequisite is already needed through another
W105 warning Shadowing that needs review: an earlier row hides part of a later one
W110 warning A first table with no overlaps
W111 warning A declaration is never used
W116 warning No example uses this sequence
W119 warning A pinned fragment is not cited
W120 warning The copy states a value no row uses
W118 warning No row of an applied table is reached in this apply
W117 warning An exception with no effect
W121 warning An identifier collides, or cannot be used, in a target language
W115 warning No element can land on this verdict
W114 warning Unconfirmed overlap: an input may match both rows
W901 warning A key is written in the file
E074 error A from sum, from max or from min, or an elements … from, is misshapen
E070 error A random input or a claim of chances is not shaped right
E073 error A pick by weight is not shaped right
E133 error A claim of chances does not hold
W129 warning A claim of chances could not be decided within the budget
W131 warning The chances function runs the rule many times on every call
E067 error A contribution is not shaped as one
E068 error An open value is not shaped as one
E131 error A value put together leaves the range it declares
W132 warning A term of an open value changes nothing

E001

error — Unterminated string

When. A string opened with " is not closed on the same line. Neither a cell nor a description may contain a line break.

Fix. Close the " on the same line. A long description either fits on one line or moves into a # comment.

Smallest reproduction:

rule t(t) v1
description "unterminated

Related codes: E002

E002

error — Unreadable character

When. A character that is neither a letter nor _ appears where a name is expected — an alias in parentheses that is not a name (sixty(60)) included. The check exists so that a typo does not quietly become a name, nor an alias quietly go missing.

Fix. Delete the character. A name starts with a letter or _ (@x(x) becomes x(x), and sixty(60) becomes sixty(size_60)).

Smallest reproduction:

rule t(t) v1

inputs
  %x(x) : bool

Related codes: E001, E009

E003

error — The file does not start with a rule line

When. One .rule is one rule, and its first line carries the name and the version. Anything else comes before it, or the rule line has no name — a file whose rule line had none used to pass the check as a rule with nothing in it.

Fix. Add rule <name>(<alias>) v1 as the first line.

Smallest reproduction:

inputs
  x(x) : bool

Related codes: E004, E011

E004

error — The line does not start with a word

When. A line that is neither a table row, a comment nor blank starts with a symbol. The syntax is line-oriented: the first word of a line decides what is being declared.

Fix. Start the line with a declaring word (description / import / enum / group / inputs / elements / outputs / derive / define / constraint / table / fold / count / sum / max / min / sequence / result / examples / policy / overrides / clause / source / apply / shape / machine / scenario / prerequisites / chances / open). A table row starts with |.

Smallest reproduction:

rule t(t) v1

= 1

Related codes: E003, E005

E005

error — A word that cannot appear at this position

When. The word at the head of the line is not in the vocabulary. The vocabulary has no synonyms: one English spelling each.

Fix. Correct it to one of description / import / enum / group / inputs / elements / outputs / derive / define / constraint / table / fold / count / sum / max / min / sequence / result / examples / policy / overrides / clause / source / apply / shape / machine / scenario / prerequisites / chances / open. Business words belong in names and cells, not at the head of a line.

Smallest reproduction:

rule t(t) v1

foo bar

Related codes: E004, E009

E006

error — The declaration has no =

When. enum, group, derive, define and result separate the name from the body with =. It is missing.

Fix. Insert the =, e.g. enum k(k) = a(a) | b(b).

Smallest reproduction:

rule t(t) v1

enum k(k) a(a) | b(b)

Related codes: E005

E007

error — No such policy

When. The word after policy is not unique or first. DMN's Any, Priority and Collect are not adopted.

Fix. Write either policy unique (every overlap is an error) or policy first (the earlier row wins).

Smallest reproduction:

rule t(t) v1

inputs
  x(x) : bool

outputs
  r(r) : bool

table j(j)
policy any
| x | -> r(r) : bool |
| - | true           |

Related codes: W110, E105, W105

E008

error — Empty cell

When. A cell of a table holds only whitespace. A blank is indistinguishable from a forgotten entry, so it is a syntax error.

Fix. If any value is meant, write -. If a value was forgotten, write the value.

Smallest reproduction:

rule t(t) v1

inputs
  x(x) : bool

outputs
  r(r) : bool

table j(j)
policy unique
| x | -> r(r) : bool |
|   | true           |

Related codes: E101

E009

error — A declared name collides with a reserved word

When. A declared name (or alias) is the same as a word of the vocabulary. Otherwise the line-oriented parser reads the line as the start of a section and drops the declaration silently. An enum value never starts a line, so for a value it is only the eleven words its own position reads as something else (not none true false starts_with default after empty exhausted by with).

Fix. Rename it (enum range(kind) becomes enum range_band(range_kind)). The reserved words are fixed by the one table in src/kw.rs.

Smallest reproduction:

rule t(t) v1

enum range(kind) = a(a) | b(b)

Related codes: E005, E011

E010

error — The .. range notation is not allowed

When. A cell contains .., as in 0g..1000g. Whether "up to 1000g" includes the endpoint cannot be read off the text.

Fix. Rewrite it with comparison operators: 0g..1000g is either <=1000g or <1000g. To fix both ends, write >=0g <=1000g.

Smallest reproduction:

rule t(t) v1

inputs
  w(w) : mass[g]  range >=0g <=10kg

outputs
  r(r) : bool

table j(j)
policy unique
| w         | -> r(r) : bool |
| 0g..1000g | true           |
| >1000g    | false          |

Related codes: E105, E101

E011

error — A public name has no ASCII alias

When. The rule name, an input, an output, a sequence or a field of one element has a non-ASCII name and no alias in parentheses. The alias becomes the public name in the generated code (a kanji has no uppercase and cannot begin an exported Go identifier, and the name of a field of the .proto is ASCII alone). A sequence is an input the caller passes, and the fields of one element are what the caller fills in, so they are as public as an input. A name that is already ASCII is its own public name and needs no alias.

Fix. Add the alias in parentheses (重量 : bool becomes 重量(weight) : bool). On a derived value, a definition, a group or a table the alias is optional: write one and the generated code uses it, leave it out and the declared name is used as it stands.

Smallest reproduction:

rule t(t) v1

inputs
  重量 : bool

outputs
  r(r) : bool

table j(j)
policy unique
| 重量 | -> r(r) : bool |
| -    | true           |

A sequence with no ASCII alias:

rule pieces_total v1

elements pièces
  amount : money[USD]  range >=0USD <=100USD

outputs
  total : money[USD]  round down(1USD)

sum spent over pièces of amount  range >=0USD <=1000USD

define total : money[USD] = spent

Related codes: E009, E012

E012

error — The name is not declared

When. A column or an expression names something that is declared nowhere. There is no forward reference: a name is declared above the line that uses it. It also appears when a value written in a cell or a group is a value of no enum; when the rule imports a built-in namespace of divisions (std/us/states and the rest), the note gives the closest spelling, or the namespace of another country that has the word. And it appears when a word is a spelling two imported countries share (WA is the code of Washington and of Western Australia). none is not one of these: under an optional column it is the absent value, and under any other column it is E103.

Fix. Match the spelling to the declaration, or declare the input, derived value or definition above. For a spelling two countries share, write the value's name (Washington or Western_Australia).

Smallest reproduction:

rule t(t) v1

inputs
  x(x) : bool

outputs
  r(r) : bool

table j(j)
policy unique
| y | -> r(r) : bool |
| - | true           |

Related codes: E011, E013

E013

error — No such import

When. The target of import is not there. There are three kinds: the built-in namespace (the first-level divisions of thirteen countries: std/us/states, std/gb/nations, std/cn/provinces, std/tw/divisions, std/kr/provinces, std/in/states, std/fr/regions, std/es/communities, std/it/regions, std/de/states, std/au/states, std/br/states and std/jp/prefectures, whose prefectures std/都道府県 also imports, with the values spelled in Japanese), an enum in a .proto (import proto "<file>" <Enum> -> <enum of this rule>), and an enum in a JSON Schema (import jsonschema "<file>" "<pointer>" -> <enum of this rule>, OpenAPI included). For the last two it appears when the file cannot be read, when it holds no such enum, or when the line is not that shape. YAML is not read; point at a JSON form of it. It also appears when one country's divisions are imported under both of its names (std/都道府県 and std/jp/prefectures).

Fix. Correct the namespace's spelling (the note gives the closest one), or declare the enum in this file with enum. When importing from a file, the path is followed from the directory of the rule file, so write it relative to that. A JSON Schema pointer looks like #/components/schemas/<name>, and one that does not resolve comes back with the keys that are there.

Smallest reproduction:

rule t(t) v1

import std/us/state

Related codes: E012, E032

E014

error — An output cell cannot hold an expression

When. A cell to the right of -> holds two or more words. Only one value or one name may be written there. Taking just the first word and skipping the rest would emit generated code with the multiplication silently dropped.

Fix. Give the calculation a name on a define line and leave only that name in the table (| - | rate_discount |). A table holds the branching and nothing else.

Smallest reproduction:

rule t(t) v1

inputs
  p(p) : money[JPY, incl_tax]  range >=0JPY <=10_000JPY
  r(r) : rate[step 1%]  range >=0% <=100%

outputs
  o(o) : money[JPY, incl_tax]  round down(1JPY)

table j(j)
policy first
| r    | -> o(o) : money[JPY, incl_tax] |
| <=5% | 0JPY                           |
| -    | p * r                          |

Related codes: E008, E012

E015

error — result can only assemble the first output

When. A result names an output other than the first. result is sugar for the first output, and both the evaluator and the generated code apply it only there. The name used to be ignored, so a number could land in a money slot without an E103.

Fix. Write a define of the same name as that output (define points(pts) : number = base_points * multiplier); outputs are taken, in declaration order, from the binding of their own name. To assemble it with result instead, move that output to the top of outputs.

Smallest reproduction:

rule t(t) v1

inputs
  p(p) : money[JPY, incl_tax]  range >=0JPY <=10_000JPY

outputs
  a(a) : money[JPY, incl_tax]  round down(1JPY)
  b(b) : money[JPY, incl_tax]  round down(1JPY)

table j(j)
policy unique
| p | -> a(a) : money[JPY, incl_tax] |
| - | 100JPY                         |

result b = p

Related codes: E016, E103

E016

error — There can be only one result

When. A file has more than one result line. Only the first output can be assembled, so a second result merely replaces the first — which it used to do in silence.

Fix. Keep one. The other outputs are taken from a define of the same name as the output.

Smallest reproduction:

rule t(t) v1

inputs
  p(p) : money[JPY, incl_tax]  range >=0JPY <=10_000JPY

outputs
  a(a) : money[JPY, incl_tax]  round down(1JPY)

table j(j)
policy unique
| p | -> a(a) : money[JPY, incl_tax] |
| - | 100JPY                         |

result a = p
result a = p + 100JPY

Related codes: E015

E017

error — A constraint is not shaped like this

When. A constraint line is not input comparison input: there is no comparison, a side is neither one name nor names added up with +, or both sides are sums.

Fix. Write constraint <input> <= <input>; the comparisons are <=, <, >= and >. One side may add inputs up (constraint discount + points <= subtotal); a coefficient or a constant cannot be written. For A = B, write the two lines A <= B and A >= B.

Smallest reproduction:

rule t(t) v1

inputs
  a(a) : number  range >=0 <=10
  b(b) : number  range >=0 <=10

constraint a

outputs
  r(r) : bool

table j(j)
policy unique
| a | -> r(r) : bool |
| - | true           |

Related codes: E018

E018

error — A constraint relates two inputs

When. A side of a constraint is not an input, or has a type with no order. A constraint says which combinations of the values the caller passes can happen, so both sides name something in inputs, and each is money, a quantity, a rate, a number or a date. Every name a sum adds is held to the same.

Fix. Name inputs on both sides. A derived or defined value is computed from inputs, so write the relation between those inputs; an enum and a boolean have no order.

Smallest reproduction:

rule t(t) v1

inputs
  a(a) : number  range >=0 <=10

constraint a <= r

outputs
  r(r) : bool

table j(j)
policy unique
| a | -> r(r) : bool |
| - | true           |

Related codes: E017, W111

E019

error — An example is an input the generated code refuses at its door

When. An example's inputs do not satisfy a constraint, or the value of an input — a field of an element of the sequence included — lies outside its declared range. A constraint declares that a combination does not happen and a range that no value outside it arrives; the completeness check believed them and demanded no row there, and the generated code refuses that input at the door. It is not an input an answer can be claimed for. An example outside the range, and one that breaks a constraint between two dates, used to pass the check, and the vector made from it was then refused by the generated code.

Fix. Correct the example's values — or, if that value or combination really does arrive, widen the range or drop the constraint.

Smallest reproduction:

rule t(t) v1

inputs
  a(a) : number  range >=0 <=10
  b(b) : number  range >=0 <=10

constraint a <= b

outputs
  r(r) : bool

table j(j)
policy unique
| a | b | -> r(r) : bool |
| - | - | true           |

examples
| a | b | -> r |
| 5 | 1 | true |

Related codes: E017, E018, E101

E020

error — The elements declaration is not right

When. An elements line has no name, or nothing walks the sequence it declares. A sequence becomes a value of the rule only when a count, a sum, a max, a min or a fold names it after over; an item that reads a field of a sequence nothing walks reads no element in particular. A rule may walk any number of sequences, each under an elements of its own.

Fix. Write elements fee_rows(fee_rows), and the fields of one element under it, declared the way inputs are. If the sequence is needed, add the line that walks it; if not, take the elements out.

Smallest reproduction:

rule t(t) v1

inputs
  n(n) : number  range >=0 <=10

elements xs(xs)
  k(k) : number  range >=0 <=10

outputs
  r(r) : bool

table j(j)
policy unique
| n | -> r(r) : bool |
| - | true           |

Related codes: E028, E021, E031

E021

error — The fold is not written correctly

When. The heading is not fold <verdict column> over <sequence> [-> <name> [: <type>]], or an arm is not one of next, stop, stop with <value>, take_unique <value>, take_first <value>, keep_max <value> by <key>, take_weighted <value> by <weight>, or the column being folded is not an enum. So is an answer named after an input, a field of an element, a sequence or a value declared above; a name that is no output given no type; an output's name given a type other than the output's; and a name that the fold and another item both decide. So, too, is a value of the fold that reads what is not there when it is decided: empty and exhausted are decided where no element is being looked at and cannot read an element's value; no value can read what is computed from the fold's own answer (a verdict that read it would need the answer before there is one); and held, what the walk holds once it has seen every element, belongs in exhausted only.

Fix. Correct the heading and the arms. A bare take cannot be written: whether one and only one element may be taken, or the first of several, is for the author to choose. To answer with a value of an element, take it with take_unique, take_first or keep_max, and answer exhausted -> held. Name the answer with -> <name> for the items below to read it; a verdict that needs a fold's answer goes in another fold, which reads it on a second walk.

Smallest reproduction:

rule t(t) v1

inputs
  n(n) : number  range >=0 <=10

elements xs(xs)
  k(k) : number  range >=0 <=10

outputs
  r(r) : bool

table j(j)
policy unique
| n | -> r(r) : bool |
| - | true           |

fold r
  empty -> false

Related codes: E020, E022, E023, E024

E022

error — The answer for a sequence with no elements is not declared

When. A fold has no empty -> <value>, or a max or a min has no empty <value>. An empty sequence always turns up, and it is the case a hand-written loop most often forgets — usually by reading the first element and falling over. For a max and a min, it is also the answer when no element passes the where.

Fix. Add empty -> <value> (empty <value> on a max or a min). What to answer is a business decision, and not one the tool can make.

Smallest reproduction:

rule t(t) v1

enum v(v) = a(a) | b(b)

elements xs(xs)
  k(k) : money[JPY, incl_tax]  range >=0JPY <=10JPY

outputs
  r(r) : money[JPY, incl_tax]  round down(1JPY)

table j(j)
policy unique
| k      | -> d(d) : v |
| <=5JPY | a           |
| >5JPY  | b           |

fold d over xs
  a -> next
  b -> take_first k
  exhausted -> held

A max with no empty:

rule t(t) v1

elements xs(xs)
  k(k) : number  range >=0 <=10

outputs
  r(r) : number  round down(1)

max r over xs of k

Related codes: E023, E024

E023

error — The answer for a walk that reached the end is not declared

When. A fold has no exhausted -> <value>: the answer when the sequence ran out and no element ended the walk. Answering with the value that was held is a choice, and it is made by writing it.

Fix. Add exhausted -> <value>; to answer with what is held, that is exhausted -> held.

Smallest reproduction:

rule t(t) v1

enum v(v) = a(a) | b(b)

elements xs(xs)
  k(k) : money[JPY, incl_tax]  range >=0JPY <=10JPY

outputs
  r(r) : money[JPY, incl_tax]  round down(1JPY)

table j(j)
policy unique
| k      | -> d(d) : v |
| <=5JPY | a           |
| >5JPY  | b           |

fold d over xs
  a -> next
  b -> take_first k
  empty -> 0JPY

Related codes: E022, E024

E024

error — Some verdict has no arm

When. A verdict the table can produce has no arm in the fold: when an element lands on it, the walk has no move. It is the table's own completeness check, applied to the fold.

Fix. Add the arm, or stop the table producing that value. The other direction — an arm for a verdict nothing can reach — is W115.

Smallest reproduction:

rule t(t) v1

enum v(v) = a(a) | b(b)

elements xs(xs)
  k(k) : money[JPY, incl_tax]  range >=0JPY <=10JPY

outputs
  r(r) : money[JPY, incl_tax]  round down(1JPY)

table j(j)
policy unique
| k      | -> d(d) : v |
| <=5JPY | a           |
| >5JPY  | b           |

fold d over xs
  a -> next
  empty -> 0JPY
  exhausted -> held

Related codes: E022, E023, W115

E025

error — The examples have no column for the sequence

When. A rule that walks a sequence has examples, but the header has no column named after its elements. An example that does not say which sequence it walks is an example with no answer.

Fix. Write the list with sequence <name>, add a column for the sequence to the examples header, and name it in the cell. A sequence with no rows is the example for a sequence with nothing in it.

Smallest reproduction:

rule t(t) v1

enum v(v) = a(a) | b(b)

elements xs(xs)
  k(k) : money[JPY, incl_tax]  range >=0JPY <=10JPY

outputs
  r(r) : money[JPY, incl_tax]  round down(1JPY)

table j(j)
policy unique
| k      | -> d(d) : v |
| <=5JPY | a           |
| >5JPY  | b           |

fold d over xs
  a -> next
  b -> take_first k
  empty -> 0JPY
  exhausted -> held

examples
| -> r |
| 0JPY |

Related codes: E026, E027

E026

error — The sequence is not written correctly

When. The columns of a sequence do not line up with the fields of elements: a column that is not a field, a field left out, one field's column twice, a ->, no sequence to be a list of, two blocks with the same name, or a cell that is not a value (a range or a -).

Fix. Make the header the fields of elements as they are, and write one element's values per row. This is not a table: it is the list of values as they would really be passed.

Smallest reproduction:

rule t(t) v1

enum v(v) = a(a) | b(b)

elements xs(xs)
  k(k) : money[JPY, incl_tax]  range >=0JPY <=10JPY

outputs
  r(r) : money[JPY, incl_tax]  round down(1JPY)

table j(j)
policy unique
| k      | -> d(d) : v |
| <=5JPY | a           |
| >5JPY  | b           |

fold d over xs
  a -> next
  b -> take_first k
  empty -> 0JPY
  exhausted -> held

sequence s(s)
| m    |
| 3JPY |

Related codes: E025, E020

E027

error — The example names a sequence that is not there

When. The cell in the sequence column names a sequence that is not declared, or holds something that is not a name at all.

Fix. Write a sequence under that name, or correct the cell to one that is written.

Smallest reproduction:

rule t(t) v1

enum v(v) = a(a) | b(b)

elements xs(xs)
  k(k) : money[JPY, incl_tax]  range >=0JPY <=10JPY

outputs
  r(r) : money[JPY, incl_tax]  round down(1JPY)

table j(j)
policy unique
| k      | -> d(d) : v |
| <=5JPY | a           |
| >5JPY  | b           |

fold d over xs
  a -> next
  b -> take_first k
  empty -> 0JPY
  exhausted -> held

examples
| xs   | -> r |
| nope | 0JPY |

Related codes: E025, E026

E028

error — The count is not written correctly

When. The line is not count <name>(<alias>) over <sequence> where <column> = <value>: no over, no where, a sequence that elements does not declare, or an = with nothing on its right. So is a sum that is not sum <name>(<alias>) over <sequence> of <column>, and a max or a min that is not <name>(<alias>) over <sequence> of <column> [where <column> [= <value>]] empty <value>.

Fix. Write it in that shape. The = <value> may be left out only for a bool column.

Smallest reproduction:

rule t(t) v1

inputs
  n(n) : number  range >=0 <=10

elements xs(xs)
  b(b) : bool

outputs
  r(r) : bool

count h(h) where b  range >=0 <=10

table j(j)
policy unique
| n | -> r(r) : bool |
| - | true           |

Related codes: E029, E030, E020

E029

error — This column cannot be counted

When. The column where names is not a value of one element (an input or a derived value is one per call, so counting it could only answer 0 or 1); or its values are not a closed set; or the value written is not one of that enum's; or an enum column was given no = <value>. The column after of on a sum, a max or a min is held the same way, and it also appears when that column is a value of another sequence than the one over names, when a sum reads no number, and when a max or a min reads neither a number, a date nor an ordered enum (their title then says the column cannot be summarised).

Fix. Name a field of an element, or a column a per-element table produces. Writing the classification as a table is what puts the classification itself under the completeness check.

Smallest reproduction:

rule t(t) v1

inputs
  n(n) : number  range >=0 <=10
  ok(ok) : bool

elements xs(xs)
  b(b) : bool

outputs
  r(r) : bool

count h(h) over xs where ok  range >=0 <=10

table j(j)
policy unique
| h | -> r(r) : bool |
| - | true           |

Related codes: E028, E012

E030

error — A count needs a range

When. The count line has no range >=0 <=<max>, or no upper bound, or a negative lower one. The range means two things: the universe the completeness check quantifies over once the count is a column, and the cap on the sequence.

Fix. Write it as range >=0 <=100. The generated code refuses a longer sequence at the door, the way it refuses a number outside its range.

Smallest reproduction:

rule t(t) v1

inputs
  n(n) : number  range >=0 <=10

elements xs(xs)
  b(b) : bool

outputs
  r(r) : bool

count h(h) over xs where b

table j(j)
policy unique
| h | -> r(r) : bool |
| - | true           |

Related codes: E028, E112

E031

error — One item reads the elements of two sequences

When. One item (a table, a clause, a derive, a definition or a fold) reads a value of one element of each of two sequences. A value of one element exists only while its sequence is walked, so reading both together would walk one sequence again from the start at every element of the other — a walk inside a walk. The number of walks would then grow with the length of a sequence, and the checks could not finish. So is an output that is a value of one element: once the walk is over, it is not one value. A rule's items fall into those that read no element and those of one element, and an item of one element is worked out on the walk after the one that finds every summary it reads (an item of one element that reads a summary goes on a second walk).

Fix. Make one of the sequences one value first — a count, a sum, a max, a min or a fold — and read that value in the item of the other; the item is then worked out on a second walk, once the value is known. To answer with something each element decides, make it one value for the whole sequence with a summary first.

Smallest reproduction:

rule t(t) v1

elements xs(xs)
  a(a) : number  range >=0 <=10

elements ys(ys)
  b(b) : number  range >=0 <=10

outputs
  r(r) : number  round down(1)
  s(s) : number  round down(1)

define both(both) : number = a + b

sum r over xs of both  range >=0 <=1000

sum s over ys of b  range >=0 <=1000

An output that is a value of one element:

rule big_parts v1

elements parts
  price : number  range >=0 <=100

outputs
  total : number  round down(1)
  big   : bool

table big_of
policy unique
| price | -> big : bool |
| >=50  | true          |
| <50   | false         |

sum total over parts of price  range >=0 <=10000

Related codes: E021, E029, E020

E032

error — The declared enum and the imported one disagree

When. The values of the enum named by import proto or import jsonschema and the values of the rule's enum (their ASCII aliases, where they have them) are not the same set. Values on either side alone are named, in both directions. It is usually the proto that gained one, and it shipped as a compatible change made outside this rule.

Fix. Add the new value to the rule's enum; where the enum gives its values names with aliases, the name is yours to choose. A value the contract dropped goes from the rule too. Once it is added, the table asks whether it needs a row (E033).

Smallest reproduction:

rule t(t) v1

import proto "tier.proto" Tier -> v
enum v(v) = one(one)

inputs
  x(x) : v

outputs
  r(r) : bool

table j(j)
policy unique
| x   | -> r(r) : bool |
| one | true           |

With tier.proto beside it:

syntax = "proto3";

enum Tier {
  TIER_UNSPECIFIED = 0;
  TIER_ONE = 1;
  TIER_TWO = 2;
}

Related codes: E013, E033, E101

E033

error — A value that came from elsewhere has neither a row nor default

When. A value of an imported enum appears in no row and is not marked default. The same holds for a value a contribution adds with extend that no row of a table names and that falls in silence to a row of -, a not: that does not leave it out, or a comparing cell that now covers it (each such row is named). For a value you wrote yourself that is a forgotten line (W111); for a value that came through a contract or a contribution it means a change from elsewhere that nobody has read yet, so it stops. With a default row the completeness check passes and the new value quietly takes the default row's answer.

Fix. Add a row for it, or mark the value default in the declaration. For a value a contribution adds, have a contribution add its row to the open table, or put default on the extend line. default is a signature saying that falling through to the default row is what is meant — that someone decided how it is treated.

Smallest reproduction:

rule t(t) v1

import proto "tier.proto" Tier -> v
enum v(v) = one(one) | two(two)

inputs
  x(x) : v

outputs
  r(r) : bool

table j(j)
policy first
| x   | -> r(r) : bool |
| one | true           |
| -   | false          |

With tier.proto beside it:

syntax = "proto3";

enum Tier {
  TIER_UNSPECIFIED = 0;
  TIER_ONE = 1;
  TIER_TWO = 2;
}

A value a contribution adds (extend):

contribution c(c) v1
to "base.rule" signature:69e01897ab91b191

extend kind with z

With base.rule beside it:

rule base(base) v1

enum kind(kind) = a(a) | b(b)  open

inputs
  k(k) : kind

outputs
  fee(fee) : number  round down(1)

table t(t)
policy first
| k | -> fee : number |
| a | 1               |
| - | 2               |

Related codes: E032, W111, E101, E067

E034

error — A name appears twice

When. A file writes one name twice among the names of one kind, and it is said at the second. The kinds are the values (the inputs, the sequences and the fields of their elements, the outputs, and what derive, define, count, sum, max, min, open and the output columns of tables and clauses decide), the tables and clauses, the enums, the values of one enum, the groups and the values one lists, the shapes, the sources, the applies, the prerequisites blocks, the labels of the rows of one table, and the columns of the heading of a table or of the examples. An output shares its name with what decides it, and several tables and clauses may decide one name; any other two values of one name stop here, an output named like an input among them. A group named like a value of an enum is one word read two ways, and stops here too. So do two names of one list that come to one identifier in the generated code (the alias of one is the alias or the name of the other), one name given two aliases, and, in an enums file, an enum's name that is another enum's alias (import enum finds an enum by either). So do two names whose identifiers differ but which every language that writes them spells as one: the types of two enums and two values of one enum (both written in PascalCase), the functions of two prerequisites blocks (in snake_case), the element types of a rule's two sequences, and a shape beside an input or a sequence among the parameters of the function that reads the caller's objects. Two that only some languages spell as one are W121. An enum read with import enum, and a group that comes with it, is held to the names of this rule and of the other import enum lines, and an input a contribution adds to the names of the rule it adds to. A fold's answer is held by E021, and the name an apply gives a callee output by E041.

Fix. Remove one of the two, or rename one (a row label and a column of a heading alike). Of two names that come to one identifier, give one another alias. An enum read with import enum can be read under another name with -> <another name>.

Smallest reproduction:

rule weight_check v1

inputs
  weight : number  range >=0 <=9
  weight : number  range >=0 <=99

outputs
  heavy : bool

table decide
policy unique
| weight | -> heavy |
| <=5    | false    |
| >5     | true     |

One value written twice in an enum:

rule free_shipping v1

enum member_kind = basic | gold | basic

inputs
  member : member_kind

outputs
  free : bool

table decide
policy unique
| member | -> free |
| basic  | false   |
| gold   | true    |

Two names that come to one identifier:

rule size_check v1

inputs
  weight(w) : number  range >=0 <=9
  width(w)  : number  range >=0 <=9

outputs
  large : bool

table decide
policy first
| weight | width | -> large |
| >5     | -     | true     |
| -      | >5    | true     |
| -      | -     | false    |

One column twice in a table's heading:

rule weight_check v1

inputs
  weight : number  range >=0 <=9

outputs
  heavy : bool

table decide
policy first
| weight | weight | -> heavy |
| <=5    | >5     | true     |
| -      | -      | false    |

Two enums whose types take one name:

rule tier_fee v1

enum tier_level = basic | gold
enum tierLevel = low | high

inputs
  member : tier_level
  band   : tierLevel

outputs
  fee : money[USD]  round down(1USD)

table fee_by_member
policy unique
| member | band | -> fee |
| basic  | -    | 1USD   |
| gold   | low  | 2USD   |
| gold   | high | 3USD   |

One label on two rows of a table:

rule t(t) v1

inputs
  a(a) : bool

outputs
  x(x) : bool

table t1(t1)
   | a     | -> x  |
r1 | true  | true  |
r1 | false | false |

Related codes: E009, E035, E021, E041, E075, W121

E035

error — The target of overrides does not exist

When. An overrides line names a table that does not exist, or one declared below this table, or a row label (table:label) the table has no row of. A line whose shape cannot be read is reported the same way. The exception is written after what it excepts, so a target is always above.

Fix. Name a table declared above, or one of its rows (label the row at its head and write table:label). Write the side that takes precedence later.

Smallest reproduction:

rule t(t) v1

inputs
  a(a) : bool

outputs
  x(x) : bool

table t1(t1)
overrides no_such_table
| a     | -> x  |
| true  | true  |
| false | false |

Related codes: E034, E036

E036

error — The target of overrides does not define the same output

When. The table an overrides line names defines a different output from this table. Precedence exists only between definitions of the same output.

Fix. Name a table that defines the same output, or make this table's output column the same one.

Smallest reproduction:

rule t(t) v1

inputs
  a(a) : bool

outputs
  x(x) : bool
  y(y) : bool

table base(base)
| a | -> x |
| - | true |

table special(special)
overrides base
| a | -> y |
| - | true |

Related codes: E035, E045

E037

error — A cited fragment is not pinned

When. A fragment cited with @source fragment has no fragment sha256:… pin line under its source line; for a file source, the line carries no sha256:…. A law cited with no article (@source alone), and a fragment name, a citation or a source line whose shape cannot be read, are reported the same way (a file beside the rule may be cited whole, @postal). A document's fragments are its tables, so table3 (the third table in document order; 表3 is its Japanese spelling) is the only name read. A fragment the language cannot read as one word is quoted (@osha "§1910.157"). Without a pin, a revised copy passes check in silence.

Fix. Once the transcribed rows are checked against the document, paste the fix.text line or run rulec source pin <file.rule> to pin the copy's digest.

Smallest reproduction:

rule t(t) v1

source 法 = law "000AC0000000001" asof 2026-04-01

inputs
  a(a) : bool

outputs
  x(x) : bool

table 表(t1)  @法 第1条
| a | -> x |
| - | true |

With sources/law/000AC0000000001@2026-04-01/MainProvision-Article_1.xml beside it:

<Article Num="1"><ArticleTitle>第一条</ArticleTitle><Paragraph Num="1"><ParagraphNum/><ParagraphSentence><Sentence>甲は、乙とする。</Sentence></ParagraphSentence></Paragraph></Article>

Related codes: E038, E039, W119

E038

error — A source fragment has changed

When. The pinned digest differs from the digest of the copy beside the rule. It fails in the pull request that refreshed the copy, and names the tables, clauses and rows that cite the fragment, which is all there is to reread.

Fix. Read the copy's diff; if the transcribed rows still hold, rewrite the pin line as fix.text says (rulec source pin writes it too). If the rows have to change, change them first.

Smallest reproduction:

rule t(t) v1

source 法 = law "000AC0000000001" asof 2026-04-01
  第1条 sha256:0000000000000000

inputs
  a(a) : bool

outputs
  x(x) : bool

table 表(t1)  @法 第1条
| a | -> x |
| - | true |

With sources/law/000AC0000000001@2026-04-01/MainProvision-Article_1.xml beside it:

<Article Num="1"><ArticleTitle>第一条</ArticleTitle><Paragraph Num="1"><ParagraphNum/><ParagraphSentence><Sentence>甲は、乙とする。</Sentence></ParagraphSentence></Paragraph></Article>

Related codes: E037, E039

E039

error — There is no copy of a source

When. The copy of a cited fragment is not beside the rule — sources/law/<law id>@<date>/<element>.xml for a law, tariff.md.fragments/table3.tsv beside the document for a document's table — or the file source itself cannot be read. check reads neither the network nor the document, so without a copy there is nothing to compare.

Fix. rulec source fetch <file.rule> fetches the fragment from e-Gov, the Japanese government's statute database, and takes the cited tables out of a document, into the copies beside the rule. Commit the copies.

Smallest reproduction:

rule t(t) v1

source 法 = law "000AC0000000001" asof 2026-04-01

inputs
  a(a) : bool

outputs
  x(x) : bool

table 表(t1)  @法 第2条
| a | -> x |
| - | true |

Related codes: E037, E038

E040

error — A file relied on differs from its pin

When. An apply heading carries no pin, or the pin differs from the callee beside the rule. The pin is meaning:… — the callee's rows and declarations with its comments, description and citations left out — and a sha256:… written before (the file's bytes) is still read. When the callee's rows are amended, the answers of the rule that applies it change too. The same holds for the signature:… on a contribution's to and after lines, missing or not matching: it is the shape of the file added to (its name, inputs and outputs, open values and outputs, open enums and blocks, and the declarations of the names the contribution reads), so a row or a comment of that file does not move it, and when the shape moves the diagnostic says what moved (when the repository's history holds the version it was pinned at). Without a pin, the change passes with nobody approving it.

Fix. See with rulec diff <old> <new> how many answers of this rule move and by how much; once the movement is approved, rewrite the heading as fix.text says or run rulec source pin <file.rule> to pin the callee again. For a contribution, read it against the new shape, then rewrite the signature: with rulec source pin <contribution>.

Smallest reproduction:

rule t(t) v1

inputs
  n(n) : number  range >=1 <=10

outputs
  y(y) : number  round down(1)

apply call(c) = "callee.rule"
  a = n
  x -> y

With callee.rule beside it:

rule callee(callee) v1

inputs
  a(a) : number  range >=1 <=10

outputs
  x(x) : number  round down(1)

table t(t)
policy unique
      | a   | -> x |
small | <=5 | 1    |
large | >5  | 2    |

A contribution's signature: differs from the shape it adds to:

contribution c(c) v1
to "base.rule" signature:0123456789abcdef

extend kind with z

With base.rule beside it:

rule base(base) v1

enum kind(kind) = a(a) | b(b)  open

inputs
  k(k) : kind

outputs
  fee(fee) : number  round down(1)

table t(t)
policy first
| k | -> fee : number |
| a | 1               |
| - | 2               |

Related codes: E037, E038, E044, E067

E041

error — The bindings of an apply do not match the callee

When. A callee input is left unbound or bound twice, a binding or an output line names something the callee does not have, the name given to a callee output is already declared in this rule, one callee output is given two names, or the apply block is not shaped as one. Substitution is written by binding every input explicitly, so a missing binding is a substitution left unwritten.

Fix. Bind each callee input with one <callee input> = <value> line, and rename an output with <callee output> -> <name>. The callee's input and output names are listed in the message.

Smallest reproduction:

rule t(t) v1

inputs
  n(n) : number  range >=1 <=10

outputs
  y(y) : number  round down(1)

apply call(c) = "callee.rule" sha256:1c554496c37f709f
  b = n
  x -> y

With callee.rule beside it:

rule callee(callee) v1

inputs
  a(a) : number  range >=1 <=10

outputs
  x(x) : number  round down(1)

table t(t)
policy unique
      | a   | -> x |
small | <=5 | 1    |
large | >5  | 2    |

Related codes: E040, E042, E043

E042

error — A binding does not agree in type

When. The type of a bound value differs from the callee input's (unit, tax kind, an enum against a number). Between two enums: a value of this rule's enum stands for no value of the callee's (values spelled the same on both sides map by themselves), a with names a value neither side has, or a literal is not a value of the callee's type. with none -> <value> is written only where this rule's value may be absent and the callee's input takes no absent value; written anywhere else, it is this code.

Fix. Make the types agree. For enums, <input> = <value> with <this rule's value> -> <callee's value>, … maps every value of this rule's enum; that is the shape of "'retirement' is read as 'end of term'". Write with none only where the value may be absent; where the callee takes the absent value, leave it out of with and pass it on as it is.

Smallest reproduction:

rule t(t) v1

enum kind(kind) = low(a) | mid(b) | high(c)

inputs
  k(k) : kind

outputs
  y(y) : number  round down(1)

apply call(c) = "enum_callee.rule" sha256:5c19644247d1b672
  a = k
  x -> y

With enum_callee.rule beside it:

rule enum_callee(enum_callee) v1

enum band(kind) = low(a) | mid(b)

inputs
  a(a) : band

outputs
  x(x) : number  round down(1)

table t(t)
policy unique
| a   | -> x |
| low | 1    |
| mid | 2    |

Related codes: E041, E043

E043

error — A value passed leaves the callee's range or constraint

When. The interval of a bound value reaches outside the callee input's range (the point outside is shown as the witness), or a constraint of the callee does not follow from this rule's declarations. For the range of an input of an ordered enum (range >=basic <=gold), it is a value the bound value can take, read as the callee's through with, that the range does not let in. What a value can take is an input's range (every value of its enum without one), a literal's value, what the rows of a table write for its output (what a name can take, for a row that writes one), and, for a max or a min of two values, what the two can take that the answer can be (a max comes to nothing before the later of their first values, a min to nothing after the earlier of their last ones). A value a fold or another expression decides is taken to be any value of its enum, since check does not work out which ones those come to, and what is passed is then said to be not known. The callee's completeness was proved over that range and constraint; outside them there is no definition. It is also a value that may be absent — an input or a field of an element declared T?, the output of a table a row of which answers none, a definition that only names one — passed to an input or a field of the callee that is not optional: the callee's tables have no answer for the absent value. Where with none -> <value> reads it as a value of the callee's, there is nothing to say. There is no fix.text: adding a row to the callee belongs to another unit of approval.

Fix. Narrow this rule's input range to the callee's, or define the region outside it in a clause of this rule; which is a business decision. For an enum, declare the same range on this rule's input (of an ordered enum), decide the value passed in a table that writes only values inside the range, or read the values outside it as values inside it with with. For a constraint, declare the same relation with constraint. For the absent value, read it as a value of the callee's with with none -> <value> (an enum), give this rule's name a type that is not optional where it always has a value, or decide the value in a table of this rule that answers none too and pass that. A number or a field of an element that can be absent needs the callee to take the absent value, which is a change to the callee.

Smallest reproduction:

rule t(t) v1

inputs
  n(n) : number  range >=0 <=10

outputs
  y(y) : number  round down(1)

apply call(c) = "callee.rule" sha256:1c554496c37f709f
  a = n
  x -> y

With callee.rule beside it:

rule callee(callee) v1

inputs
  a(a) : number  range >=1 <=10

outputs
  x(x) : number  round down(1)

table t(t)
policy unique
      | a   | -> x |
small | <=5 | 1    |
large | >5  | 2    |

The range of an input of an ordered enum:

rule t(t) v1

enum tier = basic < silver < gold < platinum

inputs
  k(k) : tier

outputs
  y(y) : number  round down(1)

apply call(c) = "tier_callee.rule" meaning:137f2373042d3154
  campaign = k
  x -> y

With tier_callee.rule beside it:

rule tier_callee(tier_callee) v1

enum tier = basic < silver < gold < platinum

inputs
  campaign(campaign) : tier  range >=basic <=gold

outputs
  x(x) : number  round down(1)

table t(t)
policy unique
| campaign | -> x |
| basic    | 1    |
| >=silver | 2    |

A value that may be absent:

rule t(t) v1

enum tier = basic < silver < gold < platinum

inputs
  m(m) : tier?

outputs
  y(y) : number  round down(1)

apply a(a) = "absent_callee.rule" meaning:38fa5f7ac6342966
  t = m
  x -> y

With absent_callee.rule beside it:

rule absent_callee(absent_callee) v1

enum tier = basic < silver < gold < platinum

inputs
  t(t) : tier

outputs
  x(x) : number  round down(1)

table t1(t1)
policy unique
| t        | -> x |
| basic    | 1    |
| >=silver | 2    |

Related codes: E041, E042, E101

E044

error — That rule cannot be applied

When. The callee cannot be read, does not pass check (the codes are listed), or the applies go round: a rule applies a rule that applies it back, directly or through others (every file of the ring is listed). A broken rule expanded is a broken rule, and a rule that reads itself through itself has no answer. A rule that applies another, one that walks a sequence, and one whose tables produce its own enums can all be applied. It is also an output of the callee that is a number that may have no value (money[JPY]?): an applied output becomes a definition rounded as the callee declares, and a value that may be absent cannot be rounded. An optional enum, truth value, date or string can be applied, and hands the absent value on.

Fix. Fix the callee first. For a ring, take one of the apply lines out.

Smallest reproduction:

rule t(t) v1

inputs
  n(n) : number  range >=1 <=10

outputs
  y(y) : number  round down(1)

apply call(c) = "broken_callee.rule" sha256:765fd7738ae6bfc1
  a = n
  x -> y

With broken_callee.rule beside it:

rule broken_callee(broken) v1

inputs
  a(a) : number  range >=1 <=10

outputs
  x(x) : number  round down(1)

table t(t)
policy unique
| a   | -> x |
| <=5 | 1    |

Applies that go round:

rule t(t) v1

inputs
  n(n) : number  range >=1 <=10

outputs
  y(y) : number  round down(1)

apply o(o) = "other.rule"
  a = n
  x -> y

With other.rule beside it:

rule other(other) v1

inputs
  a(a) : number  range >=1 <=10

outputs
  x(x) : number  round down(1)

apply back(back) = "explain.rule"
  n = a
  y -> x

Related codes: E040, E041

E045

error — A table that shares an output has two or more output columns

When. An output is defined by two or more tables (or tables are joined by overrides), and one of them has two or more output columns. A row that bundles two definitions leaves the other value's origin undecided when only one is overridden, and the generated code would have to write the row's condition twice.

Fix. Move the second output to a table of its own.

Smallest reproduction:

rule t(t) v1

inputs
  a(a) : bool

outputs
  x(x) : bool
  y(y) : bool

table base(base)
| a | -> x | y    |
| - | true | true |

table special(special)
overrides base
| a    | -> x  |
| true | false |

Related codes: E036, E105

E046

error — A clause is not shaped like this

When. The clause heading lacks a name, the -> or the output; the when or then line is missing or appears twice; or the when condition is not <column> <cell> and … (a part without a column, a column twice, a column without a condition). A clause is a one-row table, so it needs exactly one condition and one value.

Fix. Under clause <name>(<alias>) -> <output>, write one when <column> <cell> and … line (when always when there is no condition) and one then <value> line. Add overrides <target> when it takes precedence over something.

Smallest reproduction:

rule t(t) v1

inputs
  a(a) : bool

outputs
  x(x) : bool

clause exception(exception) -> x
  then true

Related codes: E008, E035, E045

E047

error — Extra token after the declaration

When. A declaration line holds a word the line does not read. Each kind of line reads its own words: an input's range, contract_only and random; a field of an element's range and contract_only; an output's range and round; a derive and a count, sum, max or min line range alone. A shape line holds nothing after its declaration, and an input's from nothing after its path but where and empty. The readers look along the line for the word they want and step over everything else, so such a word used to be dropped in silence: a tax flag written after the range, as in range >=0JPY <=10_000JPY incl_tax, what is left of a bound whose unit did not lex as one, a round on a derive, a range after a from. A range is the universe the completeness proof quantifies over and the entry guard of the generated code, so a bound lost this way is answered "complete" with one side missing.

Fix. Remove the word, or write it in the form the declaration takes. A tax flag or a step goes inside the type's brackets (money[JPY, incl_tax], rate[step 0.1%]); a range is range >=<value> <=<value>; a rounding is round <mode>(<grid>). An input's range and marks go before its from; a derived value is rounded inside its expression, as down(<expression>, 1JPY).

Smallest reproduction:

rule t(t) v1

inputs
  a(a) : money[JPY, incl_tax]  range >=0JPY <=10_000JPY incl_tax

outputs
  x(x) : bool

table t1(t1)
policy unique
| a | -> x |
| - | true |

Related codes: E011, E103, E104

E049

error — A thousands separator cannot be written

When. A number is written as a document writes it, one to three digits followed by , and groups of three: 1,000, 1,949,000JPY. Inside a cell , separates the members of a set, so as it stands the figure reads as more than one value. It used to be read that way without a word: <=1,000 in a column of numbers passed the check as <=1, and in a column of money it stopped at an E103 about 1 having no unit. Copying a document's figures as they stand lands here.

Fix. Take the commas out and write 1000; to group the digits, write 1_000. fix.text is the literal rewritten.

Smallest reproduction:

rule t(t) v1

inputs
  a(a) : money[JPY]  range >=0JPY <=5000JPY

outputs
  x(x) : bool

table t1(t1)
policy unique
| a          | -> x  |
| <=1,000JPY | true  |
| >1,000JPY  | false |

Related codes: E014, E103

E048

error — This type has no arithmetic

When. A type that is ordered but has no arithmetic is used with + - × ÷. There are three: date, temperature[℃]/temperature[℉], and sound[dB]. A ℃ has a displaced zero, so temp × 2 means nothing; a decibel is a logarithm, so adding two of them is not two sounds' worth; and a date is a calendar day, with no type to hold the result of subtracting one. All three appear in rules only as thresholds, so comparison and range are kept and the arithmetic is dropped.

Fix. Compare it against a threshold, or write it in a range. Where a difference or a multiple is itself the rule, take the computed value as an input, or look it up in a table. The days between two dates are counted on the calling side and passed in as a number or a duration.

Smallest reproduction:

rule t(t) v1

inputs
  a(a) : temperature[℃]  range >=0℃ <=40℃
  b(b) : temperature[℃]  range >=0℃ <=40℃

outputs
  x(x) : bool

derive gap(gap) : temperature[℃] = a - b  range >=-40℃ <=40℃

table t1(t1)
policy unique
| gap | -> x |
| -   | true |

Related codes: E103, E112, E115

E050

error — The machine section is not shaped right

When. The machine heading lacks a name or over <table>; the carry or initial line is missing, or the state's initial line is written twice; a line under machine is not carry, held, initial, final, never, once or always; or the rule has two machine sections.

Fix. The shape is machine <name>(<ascii>) over <table>, then carry <input> -> <output> and initial <state> (both required), and final <state>, …, never <state>, … after <state>, … and once <output> <cell> (all optional). To carry more values along, add a carry line and an initial <input> <value> line for each (E072).

Smallest reproduction:

rule t(t) v1

enum s(s) = a(a) | b(b) | c(c)
enum e(e) = fwd(fwd) | rev(rev)

inputs
  st(st) : s
  ev(ev) : e

outputs
  nx(nx) : s

table m(m)
policy unique
| st | ev  | -> nx(nx) : s |
| a  | fwd | b             |
| a  | rev | a             |
| b  | fwd | c             |
| b  | rev | a             |
| c  | -   | c             |

machine k(k) over m
  carry   st -> nx

Related codes: E051, E052, E053

E051

error — carry or over does not fit the declarations

When. The left of carry is not an input, the right is not an output, the two of the first carry line are not of one enum, the two of a later carry line are not of one type, the over table does not exist, or it does not decide the carried output. It is because the state is an enum of finitely many values that the claims about sequences of calls are decidable.

Fix. Declare the state as an input and an output of one enum, write carry <input> -> <output>, and name after over the table that has the carried output as an output column.

Smallest reproduction:

rule t(t) v1

enum s(s) = a(a) | b(b) | c(c)
enum e(e) = fwd(fwd) | rev(rev)

inputs
  st(st) : s
  ev(ev) : e

outputs
  nx(nx) : s

table m(m)
policy unique
| st | ev  | -> nx(nx) : s |
| a  | fwd | b             |
| a  | rev | a             |
| b  | fwd | c             |
| b  | rev | a             |
| c  | -   | c             |

machine k(k) over m
  carry   zz -> nx
  initial a

Related codes: E050, E052

E052

error — A state the machine names does not fit

When. An initial, final or never line names a value the enum of the carried state does not have, or never names one state on both sides — which the first call that stays in it breaks, so it cannot be what anyone means.

Fix. Spell the value as the enum does. Write different states on the two sides of never.

Smallest reproduction:

rule t(t) v1

enum s(s) = a(a) | b(b) | c(c)
enum e(e) = fwd(fwd) | rev(rev)

inputs
  st(st) : s
  ev(ev) : e

outputs
  nx(nx) : s

table m(m)
policy unique
| st | ev  | -> nx(nx) : s |
| a  | fwd | b             |
| a  | rev | a             |
| b  | fwd | c             |
| b  | rev | a             |
| c  | -   | c             |

machine k(k) over m
  carry   st -> nx
  initial a
  final   d

Related codes: E050, E126

E053

error — The once line does not fit the declarations

When. The output of once is not declared, is the carried state or another carried value, or the cell is -. A carried value is answered again on every call that keeps it, so counting it with once breaks on the first call that does. The cell is held to the output's type exactly as a table's cell is (E103 and the rest).

Fix. Name an output other than the carried state (once refund >0JPY). Say it about states with never … after ….

Smallest reproduction:

rule t(t) v1

enum s(s) = a(a) | b(b) | c(c)
enum e(e) = fwd(fwd) | rev(rev)

inputs
  st(st) : s
  ev(ev) : e

outputs
  nx(nx) : s

table m(m)
policy unique
| st | ev  | -> nx(nx) : s |
| a  | fwd | b             |
| a  | rev | a             |
| b  | fwd | c             |
| b  | rev | a             |
| c  | -   | c             |

machine k(k) over m
  carry   st -> nx
  initial a
  once    nx c

Related codes: E127, E103

E054

error — A rule that folds a sequence cannot be the step of a machine

When. A rule with a fold has a machine. Its answer depends on the whole sequence, so the inputs do not cut into finitely many columns and the transitions of one call cannot be counted — the reason diff refuses the same rule.

Fix. Read the sequence on the calling side and pass what it found (a count, a total) as an input. A rule that counts with count or sum can be the step of a machine.

Smallest reproduction:

rule t(t) v1

enum v(v) = a(a) | b(b)
enum s(s) = p(p) | q(q)

inputs
  st(st) : s

elements xs(xs)
  w(w) : money[JPY, incl_tax]  range >=0JPY <=10JPY

outputs
  r(r) : money[JPY, incl_tax]  round down(1JPY)
  nx(nx) : s

table j(j)
policy unique
| w      | -> d(d) : v |
| <=5JPY | a           |
| >5JPY  | b           |

table m(m)
policy unique
| st | -> nx(nx) : s |
| p  | q             |
| q  | q             |

fold d over xs
  a -> next
  b -> take_first w
  empty -> 0JPY
  exhausted -> held

machine k(k) over m
  carry   st -> nx
  initial p

Related codes: E050, E028

E055

error — A scenario is not shaped right

When. A scenario is written in a rule without a machine; it has no name, or two share one; it has no rows; its heading has one column twice; it has a column for a carried input (the state, or another carried value); or it lacks a column for another input. The first call starts from the initial values and every later one from what the call before it answered, so a carried input has no column.

Fix. Write the table right under scenario <name>(<ascii>). The header is every input but the carried one, then ->, then every output. With no state carried from one call to the next, write the rows as examples.

Smallest reproduction:

rule t(t) v1

inputs
  x(x) : bool

outputs
  r(r) : bool

table j(j)
policy unique
| x     | -> r(r) : bool |
| true  | false          |
| false | true           |

scenario s(s)
| x    | -> r  |
| true | false |

Related codes: E107, E111, E050

E056

error — The held line does not fit the declarations

When. held names something that is not an input, a carried input (the state, or another carried value), or one name twice. held says which inputs one case passes with the same value on every call, from its first to its last — the amount of an order, the class of the person who applied — and the check then stops offering sequences of calls that change one of them as counterexamples.

Fix. Name inputs. The carried input is replaced by the answer of the call before on every call, so it cannot be held.

Smallest reproduction:

rule t(t) v1

enum s(s) = p(p) | q(q)

inputs
  st(st) : s
  x(x)   : bool

outputs
  nx(nx) : s
  r(r)   : bool

table m(m)
policy unique
| st | x     | -> nx(nx) : s | r(r) : bool |
| p  | true  | q             | true        |
| p  | false | p             | false       |
| q  | -     | q             | false       |

machine k(k) over m
  carry   st -> nx
  held    r
  initial p
  final   q

Related codes: E050, E051, W126

E057

error — The declaration has no type

When. A line under inputs, outputs or elements, or a define or derive line, has a name and no type. Such a line used to be dropped in silence: the rule passed the check with one input fewer than its author wrote, and an example or a record that named it was then refused for a name nobody declared.

Fix. Write the type as <name>(<alias>) : <type> (: money[JPY, incl_tax], : <enum>, : bool). A numeric type needs a range too.

Smallest reproduction:

rule t(t) v1

inputs
  a(a) : bool
  b(b)

outputs
  r(r) : bool

table j(j)
policy unique
| a     | -> r(r) : bool |
| true  | false          |
| false | true           |

Related codes: E011, E047, E012

E058

error — A declaration line cannot be read

When. A line that starts with a declaring word — define, derive, result, enum, group, table and the like — cannot be read as that declaration: it has no name, something stands before its =, and so on. Such a line used to be dropped with nothing said, and a derive line was not even dropped: the check never finished.

Fix. Rewrite the line in the shape the note gives (define bulk(bulk) : bool = order_total >= 30_000JPY). Every declaration's shape is in docs/reference.md.

Smallest reproduction:

rule t(t) v1

inputs
  a(a) : bool

outputs
  r(r) : bool

derive

table j(j)
policy unique
| a     | -> r(r) : bool |
| true  | false          |
| false | true           |

Related codes: E006, E057, E059, E005

E059

error — The expression cannot be read

When. An expression — on the right of =, of -> in a fold, and so on — cannot be read to its last token: nothing on the right of an operator (x +), a ( never closed, two values with no operator between them (amount tax), a sign stuck to a number (amount -100JPY). What could be read used to become the expression and the rest was dropped: amount tax passed the check as amount, and x + took its whole declaration with it.

Fix. Supply the missing operator or parenthesis. To subtract, leave a space after the sign, as in amount - 100JPY: a sign touching the digits makes a negative number.

Smallest reproduction:

rule t(t) v1

inputs
  a(a) : money[JPY]  range >=0JPY <=100JPY
  b(b) : money[JPY]  range >=0JPY <=100JPY

outputs
  o(o) : money[JPY]  round down(1JPY)

define s(s) : money[JPY] = a b

result o = s

Related codes: E058, E118, E115

E060

error — A step is not positive

When. The step of a type (rate[step 0%]), the rounding grid of an output (round up(0JPY)) or of a rounding call (down(x, 0JPY)) is zero or negative. A grid of zero passed the check and the generated code divided by it at run time (Python stopped with ZeroDivisionError); a step of zero was quietly read as a step of one.

Fix. Write a step greater than zero (round up(1JPY), rate[step 0.1%]).

Smallest reproduction:

rule t(t) v1

inputs
  a(a) : money[JPY]  range >=0JPY <=100JPY

outputs
  o(o) : money[JPY]  round up(0JPY)

result o = a

Related codes: E104, E114, E106

E061

error — The range is empty

When. No value lies in a range: the lower end is past the upper one (range >=10JPY <=0JPY), or the two are equal and one of them is > or <. Such a range used to pass with nothing said: the completeness proof answered "complete" over nothing, and the generated code's entry refused every call.

Fix. Correct the end that was mistyped (range >=0JPY <=10JPY).

Smallest reproduction:

rule t(t) v1

inputs
  a(a) : money[JPY]  range >=10JPY <=0JPY

outputs
  o(o) : money[JPY]  round down(1JPY)

result o = a

Related codes: E112, E103, E060

E062

error — There is no such date

When. A literal has the shape of a date (2026-04-01) but no such day: the month is outside 1 to 12, or the day is past the end of its month (2026-02-30, 2026-01-99). Such a date used to be read by its shape alone: the checker counted on from the first of the month (January 99th was April 9th), the generated Python stopped at run time, and JavaScript's Date would have rolled it over quietly, so the languages disagreed.

Fix. Write a day that exists. For the end of a month, write its last day (2026-02-28).

Smallest reproduction:

rule t(t) v1

inputs
  d(d) : date  range >=2026-01-01 <=2026-12-31

outputs
  r(r) : bool

table j(j)
policy unique
| d            | -> r(r) : bool |
| <=2026-02-30 | true           |
| >2026-02-30  | false          |

Related codes: E002, E103

E063

error — The cell cannot be read

When. A cell of a table, of the examples or of a sequence holds something a cell cannot: words after a comparison (<=0JPY + false), a word after a value (false 0JPY), a word that is no value (+), a header column with no name. What could be read used to become the cell and the rest was dropped, and a cell that could not be read at all was dropped whole, so every cell after it moved one column to the left.

Fix. A cell holds one value, -, none, a comparison, a set of values separated by commas, not: with a set, or starts_with with a string. Two conditions on one column are two comparisons side by side (>=1 <10).

Smallest reproduction:

rule t(t) v1

inputs
  a(a) : money[JPY]  range >=0JPY <=1000JPY

outputs
  r(r) : bool

table j(j)
policy unique
| a               | -> r(r) : bool |
| <=100JPY + 1JPY | true           |
| >100JPY         | false          |

Related codes: E008, E010, E014, E064

E064

error — The row does not fit the header

When. A row of a table, of a sequence, of a scenario or of the examples has a different number of cells from the header's columns, or words follow its last | — the closing bar was left off. The last cell of such a row used to be dropped with nothing said, and a row whose cells did not match was read as it stood; the reference evaluator and the generated code then read the row two different ways and answered differently. In the examples, a header without a column for an output, or a row short of an expected value, is E111, and a header without the sequence's column is E025: each names the column first.

Fix. Write one cell per column and close the row with |. A column that takes any value gets -.

Smallest reproduction:

rule t(t) v1

inputs
  a(a) : bool

outputs
  r(r) : bool

table j(j)
policy unique
| a     | -> r(r) : bool |
| true  |
| false | true           |

Related codes: E063, E008, E111

E065

error — range from koyomi is misshapen or misplaced

When. What follows range from is not koyomi "<file>" date <name of a date>, or it is written on an input that is not a date, or on an output, a derive or a field of an element. The days a koyomi date comes to can only be the range of a date the caller passes.

Fix. Write range from koyomi "<file>" date <name of a date> on a date input. The file is a path from the rule's directory.

Smallest reproduction:

rule t(t) v1

inputs
  d(d) : date  range from koyomi "terms.cal"

outputs
  r(r) : bool

table j(j)
policy unique
| d | -> r(r) : bool |
| - | true           |

Related codes: E129, E130

E066

error — An enums file, or an import enum from one, is misshapen

When. A file headed enums holds a line other than description, import, enum, group and prerequisites (a table, inputs, outputs, a definition, an import enum, …); or import enum <enum> from "<file>" names a file that is not an enums file, or one that does not pass check. An enums file holds the enums rules share: it decides nothing, and no rule's function is generated from it.

Fix. Write the tables and the inputs in a rule that reads the enum with import enum … from. Start the file it reads with enums <name> v<version>, and make it pass check.

Smallest reproduction:

enums kinds v1

enum kind = chilled | ambient

table fee
policy unique
| kind | -> fee : number |
| -    | 1               |

Related codes: E013, E034

E069

error — An ordered enum, a comparison on one, or a display name is misshapen

When. A cell that compares (<v, >=v) is written in a column of an enum whose values are separated by |, and so have no order; or two values of such an enum are compared with < and the like, passed to max or min, or held to a range on an input. Or one enum separates its values with both < and |. Or a range lets no value in, or an enum field of an element is given a range, or a range is written on the line of a max or a min of an enum column. Or a value's display name is empty or holds a control character, or two values of one enum have the same display name. The values of an enum separated by < are ordered as written, and a comparison takes the values before (after) the one it is against.

Fix. If the order the values are declared in means something, separate them with < (enum tier = basic < silver < gold); if not, write values or sets. Use one separator throughout, and give each value a display name that is not empty and differs from the others'.

Smallest reproduction:

rule member_fee v1

enum tier = basic | silver | gold

inputs
  member : tier

outputs
  fee : money[USD]  round down(1USD)

table fees
policy unique
| member   | -> fee |
| >=silver | 0USD   |
| basic    | 5USD   |

Related codes: E012, E103

E071

error — The prerequisites are misshapen

When. In a prerequisites block: over names something that is not an enum; a name is not a value of the enum; a value is written in two places (on start and on a line of its own, on two lines, or twice on one line); a value is neither on start nor on a line of its own; there is no start line; or a line cannot be read. Or the table ordered by names is not there, is declared below the block, or does not test one column of the enum and write, for each value, one value of an ordered enum or one number; or ordered by is written in an enums file. Every value needs nothing (start), has a line of its own saying what it needs, or is one no case can take (requires nothing "<reason>"), so a value added without its prerequisites stops here.

Fix. Write each value in one place: a value that needs nothing on start, one that needs something on a <value> requires <prerequisite>, … line, and one no case can take as <value> requires nothing "<reason>". Declare the table of ordered by above the block, make it test the enum's column alone, and write one value of an ordered enum or one number in each row.

Smallest reproduction:

rule t(t) v1

enum k(k) = a(a) | b(b) | c(c)

inputs
  x(x) : k

outputs
  r(r) : bool

table j(j)
policy unique
| x | -> r(r) : bool |
| - | true           |

prerequisites p(p) over k
  start a
  b     requires a

Related codes: E136, E137, E138

E072

error — A carried value is not written right

When. A carry line after the first carries a number whose range lacks an end, or a date, a string or an optional value; a value is carried twice; a carried value has no initial <input> <value> line, or two, or one whose value does not fit its type or range; an initial <input> <value> line names the state or an input that is not carried; an always line is not shaped right; or an always line names something that is not a carried value, or the state itself. It is because a carried number has both ends of a range that the values a case can hold are finitely many, and the claims about sequences of calls decidable.

Fix. Give a carried number a range with both ends, as in range >=0 <=7, and every carried value the value it starts at with initial <input> <value>. An always line names the input on the left of a later carry line with a cell, joined by and, and the states it holds in after when (always days_left >=1 <=7 when in_grace). What is said of the state is said with never and final.

Smallest reproduction:

rule t(t) v1

enum s(s) = run(run) | done(done)

inputs
  st(st) : s
  n(n)   : number  range >=0 <=3

outputs
  nx(nx) : s
  nn(nn) : number  round down(1)

define more(more) : number = n + 1

table m(m)
policy first
| st  | n  | -> nx(nx) : s | nn(nn) : number |
| run | <3 | run           | more            |
| -   | -  | done          | n               |

machine k(k) over m
  carry   st -> nx
  carry   n -> nn
  initial run

Related codes: E050, E051, E135

E075

error — A word is both a value of an enum and a name

When. A word in an output cell of a table, or in an answer of a fold, is a value of the column's (the answer's) enum, and also the name of an input, an output, a field of an element, a derive, a definition or a table's output whose type is that enum. It reads either way, and which one is meant is not decided. Where the name's type is another, the word is read as the column's value (an output gold that is a number does not stop gold in a column of the enum from being the enum's value). An expected value in an example or a scenario is always a value, since nothing else can be written there.

Fix. Rename either the enum's value or the name.

Smallest reproduction:

rule carry_stage v1

enum stage = fresh | previous | done

inputs
  previous : stage
  close    : bool

outputs
  next_stage : stage

table decide
policy unique
| close | -> next_stage : stage |
| true  | done                  |
| false | previous              |

Related codes: E103, E012

E101

error — Completeness gap: some input matches no row

When. The union of the rows does not cover the declared input space. Completeness cannot be waived and is always required. A concrete input that matches no row is always attached. Where a column is a value the rule computes (a derive, a define, the output of a table above), the example gives its value, and the input that produces it too, once the reference evaluator has confirmed it. That input is worked out from the expression where a derive only adds, subtracts and multiplies by constants, and otherwise (a min or a rounding, a define, a table above) looked for among every combination of the inputs when there are no more than 16,384. A derive's values can fall finer than the units a cell is written in (amount * 10% comes in tenths), and they are read on that step. No row is asked for where no input reaches: on the column of a derive or of a define that computes a number, past what it can actually come to (its expression computed over the inputs' ranges and the values the tables above produce, and for a derive over the constraint lines too); on a column a table above decides, at values of the other columns that the rows writing its value never let come with it; on the column of a boolean define of one comparison, at the truth value it never takes over the inputs' ranges (made <= 2027-03-31 with made declared only up to that day is never false). An optional input (T?) takes the absent value too, and a row is asked for it; an optional number, date or truth value is asked for the whole of its declared range besides. An optional column a table above decides takes the absent value where a row above writes none or passes on a name that may have none, and a row is asked for it there.

Fix. Add a row that matches the witness. If a new enum value caused it, add a row for that value or a - row that catches everything. If the value needs no row of its own, mark it default in the enum declaration.

Smallest reproduction:

rule t(t) v1

enum k(k) = a(a) | b(b) | c(c)

inputs
  x(x) : k

outputs
  r(r) : bool

table j(j)
policy unique
| x | -> r(r) : bool |
| a | true           |
| b | false          |

Related codes: E102, E105, W111

E102

error — Unreachable row: the row never matches

When. No input matches the row. There are three forms, told apart in the wording. In the first, every input the row would take is already taken by an earlier row, or by a row that takes precedence through overrides. In the second, the row names a value that the upstream table never produces. In the third, the row asks a derived column, or the column of a define that computes a number, only for values outside what it can actually reach, the part inside (if any) being taken first by earlier rows; so does a row that asks a column a table above decides and a derived column for values the derive never comes to while the rows that write that value fire, and a row that asks a boolean define of one comparison for the truth value it never takes over the inputs' ranges. What a value can reach is its expression computed over the inputs' ranges and the values the tables above produce (for a derive, over the constraint lines too), not the range written on it.

Fix. If the row is the newer intent, move it above the row that covers it. If it is dead, delete it. If it names a value the upstream never emits, either add a row upstream that emits it, or delete this row. If it asks a derive or a define for values it never reaches, rewrite the condition within the values it can come to, or delete the row; widening a derive's range does not change those values. If it asks a boolean define for the truth value it never takes, rewrite the condition to the value it always takes, or delete the row.

Smallest reproduction:

rule t(t) v1

inputs
  w(w) : mass[g]  range >=0g <=10kg

outputs
  r(r) : bool

table j(j)
policy first
| w       | -> r(r) : bool |
| -       | true           |
| <=1000g | false          |

Smallest reproduction of the third form (a row outside what a derive can reach):

rule t(t) v1

inputs
  amount(a) : money[JPY, incl_tax]  range >=1JPY <=100JPY
  limit(l)  : money[JPY, incl_tax]  range >=0JPY <=100JPY

outputs
  r(r) : bool

derive excess(x) : money[JPY, incl_tax] = amount - limit  range >=-1000JPY <=1000JPY

table j(j)
policy unique
| excess         | -> r(r) : bool |
| <=0JPY         | false          |
| >0JPY <=100JPY | true           |
| >100JPY        | true           |

Smallest reproduction of the third form (a row outside what a define can reach):

rule t(t) v1

inputs
  score(p) : number  range >=0 <=200

outputs
  r(r) : bool

define share(s) : rate = score / 200

table j(j)
policy unique
| share        | -> r(r) : bool |
| <50%         | false          |
| >=50% <=100% | true           |
| >100%        | true           |

Smallest reproduction of the third form (a row asking a derive for values it never comes to while a table above writes the value the row asks for):

rule t(t) v1

enum size(z) = small(small) | large(large)

inputs
  amount(a) : money[JPY, incl_tax]  range >=1JPY <=100JPY
  limit(l)  : money[JPY, incl_tax]  range >=0JPY <=100JPY

outputs
  r(r) : bool

derive excess(x) : money[JPY, incl_tax] = amount - limit  range >=-100JPY <=100JPY

table k(k)
policy unique
| amount  | -> band(c) : size |
| <=50JPY | small             |
| >50JPY  | large             |

table j(j)
policy unique
| band  | excess  | -> r(r) : bool |
| small | <=50JPY | false          |
| small | >50JPY  | true           |
| large | -       | true           |

Smallest reproduction of the third form (a row asking a boolean define for the truth value it never takes):

rule t(t) v1

inputs
  made(d) : date  range >=2024-04-01 <=2026-12-31

outputs
  r(r) : bool

define in_period(p) : bool = made <= 2027-03-31

table j(j)
policy unique
| in_period | -> r(r) : bool |
| true      | true           |
| false     | false          |

Related codes: E101, W105, W110, E112

E103

error — Unit mismatch: values of different types are being mixed

When. An expression or a cell adds or compares values whose unit, currency or tax flag differ. money[JPY, incl_tax] and money[JPY, excl_tax] are different types too. It is also a step that cannot be read (rate[step 1g]), and a rate input that declares no step: either way, what the integer passed at runtime counts is not settled. So is a money type whose tax is a word other than incl_tax and excl_tax (money[JPY, foo]). And it is a value or a group written in a column whose enum does not have it, an output cell included, and a group that mixes the values of two enums. And it is a number in an expression that is no value of the unit it is written in: a bare decimal (amount * 0.4; a number with no unit is a number, which is whole) and a fraction of a money or quantity unit (0.5JPY). And it is the heading of a table, a clause or examples that writes a type other than the one its name already has: the declared type of an output, or the type a table or clause above decides the name in. And it is none, or a name that may have no value, written under a column that is not optional: an output, a column a table decides, an expected value. And it is a name that may have no value worked with in an expression (quoted * 2): such a value is tested in a table's cells, or handed on to an optional column as it is, and nothing else.

Fix. Move one side into a table. "A surcharge that depends on weight" is table weight_surcharge | weight | -> surcharge : money[JPY, incl_tax] |. A tax conversion is also written as a table, never as a formula. A step is written in the unit of its type (a rate input takes rate[step 1%] or rate[step 0.1%]). A tax is incl_tax with tax and excl_tax without. A rate is written with % (0.4 is 40%, which fix.text gives). Where a heading's type is not its name's, decide which is right and correct the other; a heading may also go without a type, and is then read in the name's. Where a column is to answer that there is no value, make its type T?: the declaration under outputs for an output, the heading for a column a table decides; otherwise write a value in the place of none.

Smallest reproduction:

rule t(t) v1

inputs
  w(w) : mass[g]  range >=0g <=10kg
  p(p) : money[JPY, incl_tax]  range >=0JPY <=10_000JPY

outputs
  r(r) : money[JPY, incl_tax]  round down(1JPY)

table j(j)
policy unique
| w | -> r(r) : money[JPY, incl_tax] |
| - | 100JPY                         |

result r = p + w

A table's heading that writes a type other than the output's:

rule t(t) v1

inputs
  w(w) : mass[g]  range >=0g <=10kg

outputs
  r(r) : money[JPY, excl_tax]  round down(1JPY)

table j(j)
policy unique
| w | -> r(r) : money[JPY, incl_tax] |
| - | 100JPY                         |

none under an output that is not optional:

rule t(t) v1

inputs
  x(x) : bool

outputs
  r(r) : money[JPY, incl_tax]  round down(1JPY)

table j(j)
policy unique
| x     | -> r(r) : money[JPY, incl_tax] |
| true  | 100JPY                         |
| false | none                           |

Related codes: E108, E112

E104

error — A numeric output declares no rounding

When. A quantity, money or rate output has no round, an optional one (money[JPY]?) included. Unless the fraction is declared, the generated code settles it silently. When the expression can produce a fraction, the message shows in yen how far the choice moves the answer.

Fix. Add rounding to the output declaration, e.g. round up(10JPY). There are five directions (up, down, half_up, half_down, half_even), pinned down for negative values as well.

Smallest reproduction:

rule t(t) v1

inputs
  x(x) : bool

outputs
  r(r) : money[JPY, incl_tax]

table j(j)
policy unique
| x     | -> r(r) : money[JPY, incl_tax] |
| true  | 100JPY                         |
| false | 200JPY                         |

Related codes: E106, E103

E105

error — Overlapping rows: the same input matches two or more rows

When. In a policy unique table, an input matching both rows was actually constructed. An overlap that could not be constructed falls to W114 instead. A pair that meets only on a combination the tables above never produce together, or only where no input reaches — past what a define comes to, at the truth value a boolean define never takes, where the rows of a table above leave a derive no values — is not reported: the completeness check asks no row there, and E102 calls a row that asks only for such points dead. On an optional column a comparison, a literal and a set of values match only a value that is there, while - and not: match the absent value too; so a none row meets a - row, and a not: 5 row, on the absent value.

Fix. If the outputs differ, decide which is right and fix the rows; to let the order decide, declare policy first. If even the outputs agree, delete one of the rows. Where two tables or clauses that define one output overlap, write overrides <the other> on the one that wins: after policy on a table, as a line under a clause. The overlap is refused even when the two answer the same, since an edit tomorrow may change one of them and not the other.

Smallest reproduction:

rule t(t) v1

enum k(k) = a(a) | b(b)

inputs
  x(x) : k
  y(y) : bool

outputs
  r(r) : money[JPY, incl_tax]  round down(1JPY)

table j(j)
policy unique
| x | y     | -> r(r) : money[JPY, incl_tax] |
| a | -     | 100JPY                         |
| - | true  | 200JPY                         |
| b | false | 300JPY                         |

Related codes: W105, W114, E102

E106

error — An output literal is not on the rounding grid

When. A literal in an output cell is not a multiple of the declared rounding grid. This is where a mistyped digit — 1451JPY in a table rounded up(10JPY) — is stopped.

Fix. Put the literal on the grid (1451JPY becomes 1450JPY or 1460JPY). If the amount really is right, change the grid instead.

Smallest reproduction:

rule t(t) v1

inputs
  x(x) : bool

outputs
  r(r) : money[JPY, incl_tax]  round up(10JPY)

table j(j)
policy unique
| x     | -> r(r) : money[JPY, incl_tax] |
| true  | 1451JPY                        |
| false | 1000JPY                        |

Related codes: E104

E107

error — An example does not match

When. Running a row of examples through the reference evaluator gives something other than the value written. Which row of which table fired is attached. examples is an executable specification. An expected none under an optional output expects no value.

Fix. If the table is right, fix the expected value. If the expected value is the business truth, fix the row that fired. Which one to fix is settled by the source: the written rule, the spreadsheet, or the legacy implementation.

Smallest reproduction:

rule t(t) v1

inputs
  x(x) : bool

outputs
  r(r) : money[JPY, incl_tax]  round down(1JPY)

table j(j)
policy unique
| x     | -> r(r) : money[JPY, incl_tax] |
| true  | 100JPY                         |
| false | 200JPY                         |

examples
| x    | -> r   |
| true | 200JPY |

Related codes: E111, E105

E108

error — Cannot prove an intermediate value fits in int64

When. The reachable interval computed from the declared ranges and steps exceeds int64. With a rate step of 1%, the stored integer is 100 times the value. So is a rule whose random inputs (random) have more than 2^62 points together: a chance is worked out by counting the points, and every language holds the count in an int64.

Fix. Narrow the input ranges, or insert one rounding step along the way. Where to round is a business decision that moves yen, so the tool does not decide it. Where the random inputs have too many points, narrow their ranges.

Smallest reproduction:

rule t(t) v1

inputs
  p(p) : money[JPY, incl_tax]  range >=0JPY <=100_000_000_000_000_000JPY
  q(q) : rate[step 1%]  range >=0% <=100%

outputs
  r(r) : money[JPY, incl_tax]  round down(1JPY)

define off(off) : money[JPY, incl_tax] = p * q

table j(j)
policy unique
| off | -> r(r) : money[JPY, incl_tax] |
| -   | 0JPY                           |

The points of the random inputs:

rule t(t) v1

inputs
  a(a) : number  range >=0 <=9999999999  random
  b(b) : number  range >=0 <=9999999999  random

outputs
  r(r) : bool

table j(j)
policy unique
| a   | b | -> r(r) : bool |
| <5  | - | true           |
| >=5 | - | false          |

Related codes: E112, E103

E109

error — The check exceeded its budget, so completeness could not be proven

When. The region check visited more nodes than --budget allows. Failing to prove something is never green here, so this is an error and not a warning.

Fix. Split the table to reduce the number of columns, or raise --budget (both rulec check and ritsu check take it). The cost is the product of the columns, so chaining tables in a linear pipeline is cheaper than piling columns into one table.

Smallest reproduction (with --budget 1):

rule t(t) v1

enum k(k) = a(a) | b(b) | c(c)

inputs
  x(x) : k

outputs
  r(r) : bool

table j(j)
policy unique
| x | -> r(r) : bool |
| a | true           |
| b | false          |
| c | true           |

Related codes: E101, W114

E110

error — A column has a type the check cannot handle

When. The column's type cannot be lowered into the region IR. Seeing this is a bug in rulec itself. It is the internal breakwater that stops a table from being skipped silently, put in after the same accident happened twice, with dates and with optional.

Fix. Change the column to a type the check handles today (bool, enum, quantity, money, rate, date, optional). Then report it: this check exists because stopping is better than passing silently.

Smallest reproduction:

rule t(t) v1

inputs
  s(s) : string?

outputs
  r(r) : bool

table j(j)
policy unique
| s               | -> r(r) : bool |
| starts_with "a" | true           |

Related codes: E101, E105

E111

error — The examples have no column for an output

When. examples writes only some of the declared outputs. Agreement across the implementations stays green when they all carry the same mistake, so only a human-written expectation can break it. Rounding for multiple outputs really did go missing in every one at once.

Fix. Add the missing output column to examples. With two or more outputs, writing -> before the later columns is optional.

Smallest reproduction:

rule t(t) v1

inputs
  x(x) : bool

outputs
  ok(ok) : bool
  fee(fee) : money[JPY, incl_tax]  round down(1JPY)

table j(j)
policy unique
| x     | -> ok(ok) : bool | fee(fee) : money[JPY, incl_tax] |
| true  | true             | 100JPY                          |
| false | false            | 0JPY                            |

examples
| x    | -> ok |
| true | true  |

Related codes: E107

E112

error — The range of a derived value does not contain the values it can reach

When. The interval computed from the input ranges falls outside the range declared on the derived value. With too narrow a range, the completeness check answers "complete" without ever looking at values that really occur. So does a range written on a max or a min that leaves out part of its column's range or its empty value, since it answers one of those.

Fix. Widen the range to the reachable interval the message states (it is written out, e.g. range >=-1_100_000JPY <=1_000_000JPY). Widening past what is reachable costs nothing: the check sifts the infeasible part out.

Smallest reproduction:

rule t(t) v1

inputs
  a(a) : money[JPY, incl_tax]  range >=0JPY <=1_000_000JPY
  b(b) : money[JPY, incl_tax]  range >=0JPY <=1_000_000JPY

outputs
  r(r) : bool

derive gap(gap) : money[JPY, incl_tax] = a - b  range >=0JPY <=1_000_000JPY

table j(j)
policy unique
| gap    | -> r(r) : bool |
| <=0JPY | false          |
| >0JPY  | true           |

Related codes: E108, E101

E113

error — The condition of a boolean definition is neither of the two allowed forms

When. The condition of define … : bool is neither one input or derived value compared with a constant, nor a comparison of two values whose difference cannot be subtracted (two dates, say). Comparing two numbers directly is the usual case.

Fix. Declare the difference as a derived value and compare that against a constant. define bigger : bool = a >= b becomes derive gap(gap) : money[JPY, incl_tax] = a - b range … and the cell >=0JPY. The analysis is exact that way.

Smallest reproduction:

rule t(t) v1

inputs
  a(a) : money[JPY, incl_tax]  range >=0JPY <=1_000_000JPY
  b(b) : money[JPY, incl_tax]  range >=0JPY <=1_000_000JPY

outputs
  r(r) : bool

define bigger(bigger) : bool = a >= b

table j(j)
policy unique
| bigger | -> r(r) : bool |
| true   | true           |
| false  | false          |

Related codes: E112, E103

E114

error — A value does not sit on the declared step

When. A value that is not a whole number of the declared step is written in the column, such as 0.5% where the type says rate[step 1%]. At runtime the value is one integer count of that step, so this one has no representation. The same holds for the rounding grid of a rate output that is not a whole number of the step the output declares (rate[step 1%] with round down(0.5%)): every answer sits on the grid and is handed over as a whole number of the step, so some answers could not be written.

Fix. Write a value on the step, or declare a finer step (rate[step 0.1%]). Quietly moving it to the nearest step would make the boundary on the page differ from the boundary in the generated code. For a rounding grid, declare the step the rounding needs, or round to a whole number of the step.

Smallest reproduction:

rule t(t) v1

inputs
  r(r) : rate[step 1%]  range >=0% <=100%

outputs
  o(o) : bool

table j(j)
policy first
| r      | -> o(o) : bool |
| <=0.5% | true           |
| -      | false          |

Related codes: E103, E106

E115

error — The divisor is not a positive constant

When. The right of ÷ is neither a positive whole constant nor a positive constant amount or quantity in the same unit: a variable, zero, a negative number or a fraction. A division that is the outermost operation of a rounding function's argument may divide by a value (half_up(total / units, 1JPY); E132 sees that the divisor is never zero). The divisor n of mod(x, n) is this too when it is not a positive whole constant.

Fix. To divide by a value, put the division inside a rounding function, with the grid and the way to round (half_up(total / units, 1JPY)). If the divisor is business data that can come as a rate, take it as a rate input, or look the constant up in a table. Without a statically known step the generated code falls back on the language's own division, and Python rounding toward -inf and Go toward zero disagree.

Smallest reproduction:

rule t(t) v1

inputs
  n(n) : number  range >=0 <=100
  d(d) : number  range >=1 <=100

outputs
  o(o) : bool

define r(r) : number = n ÷ d

table j(j)
policy first
| r | -> o(o) : bool |
| - | true           |

Related codes: E103, E108

E116

error — A row's amount is not in the copy it cites, or is under another heading there

When. The output value of a row is nowhere in the copy the row or its table cites with @source table1. Where the copy has a heading that says exactly a word of the row's cells (Kanto), only the row and the column under that heading are searched: the amount of the next row is somewhere in the copy too, and a search of the whole table would let the two be mixed up. Only amounts are compared: a threshold is rewritten as it is transcribed (up to 1,949,000JPY becomes <=1949000JPY) and an amount is not. A copy's 5/1,000 (or, in Japanese, 1,000分の5) reads as 0.5%. The copy is what rulec source fetch took out of the document; check does not read the document itself.

Fix. Reread the copy and correct the amount. When it is said to be under another heading, the rows were mixed up. For a mistyped digit W120 usually comes with it, naming the value left unused. If the value came from somewhere else — a later notice, a correction, an answer from a person — take the citation off this row and write where it came from in a comment at the end of it, which rulec doc shows on the page for people.

Smallest reproduction:

rule t(t) v1

source tariff = file "tariff.md" sha256:583beb1ff0102e2e
  table1 sha256:07423c36f9e9259f

inputs
  a(a) : bool

outputs
  x(x) : money[JPY]  round down(1JPY)

table t1(t1)  @tariff table1
policy unique
| a     | -> x   |
| true  | 990JPY |
| false | 890JPY |

With tariff.md beside it:

# Tariff

| Destination | Fare |
|---|---|
| Kinki | 990JPY |
| Kanto | 880JPY |

With tariff.md.fragments/table1.tsv beside it:

Destination Fare
Kinki   990JPY
Kanto   880JPY

Related codes: W120, E038, E107

E117

error — A share without what a share needs

When. The three of allocate(<amount>, <running total>, <whole>) are not the shape a share needs. All three are names with declared ranges, the amount and the running total cannot be negative, the whole is positive, and a constraint says the running total never passes the whole.

Fix. Write what is missing. Without constraint <running total> <= <whole> a share can exceed the amount being handed out and the lines no longer add up to the total. Below zero the targets disagree about which way to round.

Smallest reproduction:

rule t(t) v1

inputs
  off(off) : money[JPY]  range >=0JPY <=1000JPY
  upto(upto) : money[JPY]  range >=0JPY <=1000JPY
  base(base) : money[JPY]  range >=1JPY <=1000JPY

outputs
  o(o) : money[JPY]  round down(1JPY)

derive share(share) : money[JPY] = allocate(off, upto, base)  range >=0JPY <=1000JPY

result o = share

Related codes: E115, E108

E118

error — The call is not written correctly

When. A call to a function that does not exist, or with the wrong number of arguments. The calls are min(a, b), max(a, b), allocate(<amount>, <running total>, <whole>), mod(x, n), isqrt(x) and the five rounding modes (down(x, 1JPY) and the rest). A rounding grid that is not a constant is this too.

Fix. Check the spelling and the count. A spare argument is dropped on the floor and a missing one leaves no answer — both used to pass unnoticed, and only the generator ran out of cases.

Smallest reproduction:

rule t(t) v1

inputs
  n(n) : number  range >=0 <=100
  d(d) : number  range >=1 <=100

outputs
  o(o) : number  round down(1)

define r(r) : number = min(n, d, n)

result o = r

Related codes: E103, E115

E120

error — A from does not fit the input's type

When. What a from yields does not fit the input that takes it. any and all yield a bool, count yields a number, sum a number (a number, money, a quantity or a rate), and max and min a number or a date. It is also this code when the type at the end of the path does not fit the input — a field the contract calls a string taken by a number input — when any, all, count, sum, max or min would walk something that is not a collection, and when the value of a where does not fit the field; when the field of an element of reads does not fit the input (sum adds whole numbers, and a fraction cannot be added up exactly); when a sequence is taken from something that is not a collection; and when a field of an element does not fit the field it is read into. A contract says how a value travels: an enum and a date arrive as strings, and money and a quantity as whole numbers in the unit the rule declares.

Fix. Correct the type or the from. A count is taken by a number input with a range, whether the elements passed by a bool, and the value itself by from <shape>.<field>. A where value carries no unit: a contract has none, and comparing a scaled number with a raw one is the thing this must not do quietly.

Smallest reproduction:

rule t(t) v1

shape order(order) = jsonschema "order.json" "#/$defs/Order"

inputs
  a(a) : bool  from count order.lines

outputs
  x(x) : bool

table t1(t1)
policy unique
| a | -> x |
| - | true |

With order.json beside it:

{"$defs":{"Order":{"type":"object","properties":{"lines":{"type":"array","items":{"type":"object","properties":{"category":{"type":"string"}}}}}}}}

Related codes: E121, W122, E103

E121

error — The contract has no such path

When. A from path is not in the contract of the shape it starts at: the first word is not the name of a shape, a field along the way is not there, or the field a where tests or an of reads is not a field of an element. The same goes for the path of a sequence taken with elements … from, and for the path of each field of its elements, shown after [] inside the element (order.lines[].qty). The message says how far it resolved and which names were there. A path that goes on through a collection — an array in a JSON Schema, a repeated field in a .proto — is this code too: a path reads one value, and each element has its own, so which one the input takes is not said. The message names every collection along the path. The contract may be a .proto or a JSON Schema, is read on every check like import proto, and carries no pin.

Fix. Correct the spelling, or rewrite the path if the contract moved. This firing is the point: a contract that renamed a field goes unnoticed in hand-written glue until it runs, and stops the build here. Where the path goes through a collection, read the collection instead: test a field of its elements with from any <collection> where <field> = <value> (or all, count), add up or compare a number with sum, max or min, or walk the elements with elements <name> from <collection>. Where the collection always holds one element, make it one object in the contract (a field that is not repeated).

Smallest reproduction:

rule t(t) v1

shape order(order) = jsonschema "order.json" "#/$defs/Order"

inputs
  a(a) : bool  from order.nope

outputs
  x(x) : bool

table t1(t1)
policy unique
| a | -> x |
| - | true |

With order.json beside it:

{"$defs":{"Order":{"type":"object","properties":{"lines":{"type":"array","items":{"type":"object","properties":{"category":{"type":"string"}}}}}}}}

A path that goes on through a collection:

rule t(t) v1

shape order(order) = jsonschema "order.json" "#/$defs/Order"

inputs
  a(a) : string  from order.lines.category

outputs
  x(x) : bool

table t1(t1)
policy unique
| a | -> x |
| - | true |

With order.json beside it:

{"$defs":{"Order":{"type":"object","properties":{"lines":{"type":"array","items":{"type":"object","properties":{"category":{"type":"string"}}}}}}}}

Related codes: E120, W122, E032

E119

error — A row's boundary falls on the other side from the copy it cites

When. A threshold of a row that cites puts its boundary value on the other side from the copy. A threshold is rewritten as it is transcribed (up to 1,949,000JPY becomes <=1949000JPY) so the text cannot be compared; what is compared is which of the two bands the boundary value falls in. The copy's up to 60cm and over 60cm both put 60cm in the band below, and so do <=60cm and >60cm. A number the copy bounds with no word (18 to 20, 60-80), with the word in another column (a Japanese insurance premium table puts 円以上, "yen or more", in the heading over its own column), or with words on both sides, is left alone.

Fix. Reread the copy and correct it. fix.text is this cell with that one boundary's side swapped and nothing else: the direction is the table's geometry, not the copy's to decide, so only < and <= are exchanged. One boundary mistranscribed is reported on both of the rows that share it. If the boundary came from somewhere else — a later notice, a proviso in the text — take the citation off this row and say in a comment at the end of it where it came from.

Smallest reproduction:

rule t(t) v1

source sizes = file "sizes.md" sha256:8a2a4c38c68dca29
  table1 sha256:34cc5df35a840201

inputs
  a(a) : length[cm]  range >=1cm <=80cm

outputs
  x(x) : money[JPY]  round up(10JPY)

table t1(t1)  @sizes table1
policy unique
| a             | -> x    |
| <60cm         | 1410JPY |
| >=60cm <=80cm | 1710JPY |

With sizes.md beside it:

# Sizes

| Size | Fare |
|---|---|
| up to 60cm | 1410JPY |
| over 60cm up to 80cm | 1710JPY |

With sizes.md.fragments/table1.tsv beside it:

Size    Fare
up to 60cm  1410JPY
over 60cm up to 80cm    1710JPY

Related codes: E116, W120, E105

W122

warning — No input is projected from that shape

When. A shape is declared and no input or sequence says from <that name>.…. The contract is read and holds nothing.

Fix. Use it or delete it. A contract that is only read makes the next reader believe this rule is held to it; what is held to it is the inputs that say from.

Smallest reproduction:

rule t(t) v1

shape order(order) = jsonschema "order.json" "#/$defs/Order"

inputs
  a(a) : bool

outputs
  x(x) : bool

table t1(t1)
policy unique
| a | -> x |
| - | true |

With order.json beside it:

{"$defs":{"Order":{"type":"object","properties":{"lines":{"type":"array","items":{"type":"object","properties":{"category":{"type":"string"}}}}}}}}

Related codes: E121, W111

E122

error — The contract lets through a value the rule refuses

When. A value read with from can pass the contract's validation and still be refused by the input's declaration. What is compared: the range of a number (Protovalidate's gte, lte and the rest; JSON Schema's minimum and maximum), the length of a collection (min_items and max_items; minItems and maxItems), the values of an enum (string.in; enum — held to the values inside its range when an ordered enum input has one), and JSON Schema's required. A from sum is held by how far its total reaches — the values of the field times how many elements there may be — and a from max or from min by the values of the field. A sequence taken with elements … from has each field of its elements held as an input is, its length held to the cap its counts put on it, and its totals to the upper end of each sum over it. A proto3 number field with no rule arrives as 0 when it is left unset, so an input that does not take 0 stops here. A message field that is not required, and an optional field, may be left unset, and the value under it then arrives as its default (0, "", no elements) with no rule applied; a date read from a .proto string is held to whether "" passes. A condition across fields — a CEL expression, a oneof, JSON Schema's allOf, anyOf, oneOf, not and if — is read too where it narrows one field, and a JSON Schema type that has null in it is this code when the input is not optional. A rule that cannot be read, such as CEL with a remainder or a string function, is read as not there: the contract is then read wider than it is, so this may speak where it did not need to, and never stays quiet where it should have spoken.

Fix. Which side to change is a person's decision. If the value cannot occur, narrow the contract: fix.text is the annotation to write there (narrow_contract). If it can, widen the rule's range or add the value to the enum, and decide what it answers. Some preconditions cannot be written in a contract, such as a floor on how many elements a where picks out; fix.kind is then none. When the value read may be missing, make the input T?: a missing value is then read as none.

Smallest reproduction:

rule t(t) v1

shape order(order) = jsonschema "order.json" "#/$defs/Order"

inputs
  a(a) : number  range >=0 <=5  from count order.lines

outputs
  x(x) : bool

table t1(t1)
policy unique
| a | -> x |
| - | true |

With order.json beside it:

{"$defs":{"Order":{"type":"object","properties":{"lines":{"type":"array","maxItems":10,"items":{"type":"object"}}},"required":["lines"]}}}

Related codes: W123, E123, E121, E032

W123

warning — A row is reached only by values the contract does not let through

When. A cell of a row tests an input read with from, and nothing the cell accepts passes the contract's validation. Only what passed the contract arrives, so no request or message reaches the row. Only a column of the input itself is compared; a column derived from it is not.

Fix. If the contract will not widen, delete the row and bring the input's range in line with the contract. If the row is kept for a widening that is planned, leave it: check --diff-base in CI reports only the ones that are new.

Smallest reproduction:

rule t(t) v1

shape order(order) = jsonschema "order.json" "#/$defs/Order"

inputs
  a(a) : number  range >=0 <=20  from count order.lines

outputs
  x(x) : bool

table t1(t1)
policy unique
| a    | -> x  |
| <=10 | true  |
| >10  | false |

With order.json beside it:

{"$defs":{"Order":{"type":"object","properties":{"lines":{"type":"array","maxItems":10,"items":{"type":"object"}}},"required":["lines"]}}}

Related codes: E122, W124, E102, W111

E123

error — The contract lets through a combination the rule's constraint refuses

When. Both sides of a constraint are inputs read with from from the same shape, and some request passes the contract's validation with both values inside the inputs' ranges and the constraint broken. The conditions the contract places across its fields — CEL on a .proto message, a oneof, JSON Schema's combinators — are read, and what is asked is whether a breaking combination survives them. When one does, its values are the example. A rule that cannot be read is read as not there, so nothing is missed.

Fix. Which side to change is a person's decision. If the combination cannot occur, promise it in the contract: for a .proto, fix.text is the (buf.validate.message).cel to write on the message (narrow_contract). JSON Schema has no way to compare the values of two fields, so there fix.kind is none. If it can occur, take the constraint off and decide in the tables what the rule answers for it.

Smallest reproduction:

rule t(t) v1

shape quote(q) = proto "quote.proto" shop.v1.Quote

inputs
  minimum(min_g) : mass[g]  range >=1g <=30kg  from quote.min_g
  maximum(max_g) : mass[g]  range >=1g <=30kg  from quote.max_g

constraint minimum <= maximum

outputs
  x(x) : bool

table t1(t1)
policy unique
| minimum | -> x |
| -       | true |

With quote.proto beside it:

syntax = "proto3";
package shop.v1;

message Quote {
  option (buf.validate.message).cel = {id: "express_cap", expression: "!this.express || this.weight_g <= 5000"};
  int64 min_g = 1 [(buf.validate.field).int64 = {gte: 1, lte: 30000}];
  int64 max_g = 2 [(buf.validate.field).int64 = {gte: 1, lte: 30000}];
  int64 weight_g = 3 [(buf.validate.field).int64 = {gte: 1, lte: 30000}];
  bool express = 4;
}

Related codes: E122, W124, E018

W124

warning — A row is reached only by a combination the contract does not let through

When. A row's cells test two or more inputs read with from from the same shape, each cell alone asks for values the contract lets through, and under the conditions the contract places across its fields no combination of them passes: a row asking for a minimum above 20kg and a maximum of at most 10kg under the CEL this.min <= this.max, or a row asking for two members of one oneof to be both non-zero. The cells on other columns are left out, so this may call a row reachable that is not, never the other way round.

Fix. If the contract will not change, delete the row. If the row is kept for a change that is planned, leave it: check --diff-base in CI reports only the ones that are new.

Smallest reproduction:

rule t(t) v1

shape quote(q) = proto "quote.proto" shop.v1.Quote

inputs
  weight(weight) : mass[g]  range >=1g <=30kg  from quote.weight_g
  express(express) : bool  from quote.express

outputs
  fee(fee) : money[JPY]  round up(10JPY)

table fees(fees)
policy first
| express | weight | -> fee  |
| true    | >5kg   | 3000JPY |
| true    | -      | 1500JPY |
| false   | -      | 800JPY  |

With quote.proto beside it:

syntax = "proto3";
package shop.v1;

message Quote {
  option (buf.validate.message).cel = {id: "express_cap", expression: "!this.express || this.weight_g <= 5000"};
  int64 min_g = 1 [(buf.validate.field).int64 = {gte: 1, lte: 30000}];
  int64 max_g = 2 [(buf.validate.field).int64 = {gte: 1, lte: 30000}];
  int64 weight_g = 3 [(buf.validate.field).int64 = {gte: 1, lte: 30000}];
  bool express = 4;
}

Related codes: W123, E123, E102

E124

error — A final state has a way out

When. A call moves a case out of a state the final line names: a case that had ended is set going again. The shortest sequence of calls to that state, and the call that leaves it, come with it as witness.trace.

Fix. Keep the state where it is on that row, or take it off the final line. Which of the two is right is the business's to say.

Smallest reproduction:

rule t(t) v1

enum s(s) = a(a) | b(b) | c(c)
enum e(e) = fwd(fwd) | rev(rev)

inputs
  st(st) : s
  ev(ev) : e

outputs
  nx(nx) : s

table m(m)
policy unique
| st | ev  | -> nx(nx) : s |
| a  | fwd | b             |
| a  | rev | a             |
| b  | fwd | c             |
| b  | rev | a             |
| c  | -   | c             |

machine k(k) over m
  carry   st -> nx
  initial a
  final   b, c

Related codes: E125, E126

E125

error — A case can reach a state it can never finish from

When. A state a case can reach from initial is not final, and no final state can be reached from it: the "option to complete" of workflow-net soundness is broken. The shortest sequence of calls that gets there comes with it. A case whose carried number has left its range, so that no further call can take it, is E134's.

Fix. Add a row that moves on to a final state, or, if a case rightly ends there, add the state to final.

Smallest reproduction:

rule t(t) v1

enum s(s) = a(a) | b(b) | c(c) | d(d)
enum e(e) = fwd(fwd) | rev(rev)

inputs
  st(st) : s
  ev(ev) : e

outputs
  nx(nx) : s

table m(m)
policy unique
| st | ev  | -> nx(nx) : s |
| a  | fwd | b             |
| a  | rev | d             |
| b  | fwd | c             |
| b  | rev | a             |
| c  | -   | c             |
| d  | -   | d             |

machine k(k) over m
  carry   st -> nx
  initial a
  final   c

Related codes: E124, W125, E134

E126

error — A never line is broken

When. For never A after B, some sequence of calls from initial reaches A after it has been in B. The shortest one comes with it, and every call in it is an input the rule takes and answers.

Fix. Correct the rows that decide where a case goes, or drop the never line if the claim is wrong as business.

Smallest reproduction:

rule t(t) v1

enum s(s) = a(a) | b(b) | c(c)
enum e(e) = fwd(fwd) | rev(rev)

inputs
  st(st) : s
  ev(ev) : e

outputs
  nx(nx) : s

table m(m)
policy unique
| st | ev  | -> nx(nx) : s |
| a  | fwd | b             |
| a  | rev | a             |
| b  | fwd | c             |
| b  | rev | a             |
| c  | -   | c             |

machine k(k) over m
  carry   st -> nx
  initial a
  final   c
  never   c after b

Related codes: E052, E124

E127

error — A once line is broken

When. For once <output> <cell>, one case has two calls whose output the cell accepts: a refund paid twice, a point granted twice. The shortest such sequence comes with it.

Fix. Add the row that cuts the sequence short before the second call (a case that has been refunded does not go back, say), or drop the once line if it is wrong.

Smallest reproduction:

rule t(t) v1

enum s(s) = a(a) | b(b) | c(c)
enum e(e) = fwd(fwd) | rev(rev)

inputs
  st(st) : s
  ev(ev) : e

outputs
  nx(nx) : s
  f(f) : bool

table m(m)
policy unique
| st | ev  | -> nx(nx) : s | f(f) : bool |
| a  | fwd | b             | false       |
| a  | rev | a             | false       |
| b  | fwd | c             | false       |
| b  | rev | a             | true        |
| c  | -   | c             | false       |

machine k(k) over m
  carry   st -> nx
  initial a
  final   c
  once    f true

Related codes: E053, E126

E128

error — The machine's claims could not be checked

When. The inputs cut into more cells than the budget allows (--budget divided by 50), or the rule's answer does not cut into finitely many columns. A claim that was not proven is never green, so this is an error and not a warning.

Fix. Raise --budget (both rulec check and ritsu check take it), or give the table that decides the transitions fewer columns. The cost is the product of the columns, so a chain of tables is cheaper than one wide one.

Smallest reproduction (with --budget 100):

rule t(t) v1

enum s(s) = a(a) | b(b) | c(c)
enum e(e) = fwd(fwd) | rev(rev)

inputs
  st(st) : s
  ev(ev) : e

outputs
  nx(nx) : s

table m(m)
policy unique
| st | ev  | -> nx(nx) : s |
| a  | fwd | b             |
| a  | rev | a             |
| b  | fwd | c             |
| b  | rev | a             |
| c  | -   | c             |

machine k(k) over m
  carry   st -> nx
  initial a
  final   c

Related codes: E109, W127

E129

error — This rulec reads no koyomi file

When. A rule that takes a range from a koyomi date (range from koyomi) is checked by a rulec with no koyomi joined: the binary of rulec's own crate. The check does not fall back to every day of the range; the file counts as not checked, and the run exits 2.

Fix. Run ritsu rulec check <file> or ritsu check <dir>; both hand the set of days over through koyomi's port.

Smallest reproduction:

rule t(t) v1

inputs
  d(d) : date  range from koyomi "terms.cal" date payment

outputs
  r(r) : bool

table j(j)
policy unique
| d | -> r(r) : bool |
| - | true           |

With terms.cal beside it:

dates terms v1

inputs
  received : date  range >=2026-01-01 <=2026-12-20

date closing = received
  close day 20

date payment = closing
  day 10 of month +1

Related codes: E065, E130

E130

error — The days of the koyomi date cannot be read

When. koyomi does not answer for the days of the date range from koyomi names: the file is not there, does not pass koyomi's check, or has no date of that name; the inputs come to more combinations than koyomi checks; the computation stops at some input; or the date comes to no day at all. What koyomi says is in the notes. The check does not fall back to every day of the range.

Fix. Make the koyomi file pass ritsu koyomi check, and name a date it has.

Smallest reproduction:

rule t(t) v1

inputs
  d(d) : date  range from koyomi "terms.cal" date due

outputs
  r(r) : bool

table j(j)
policy unique
| d | -> r(r) : bool |
| - | true           |

With terms.cal beside it:

dates terms v1

inputs
  received : date  range >=2026-01-01 <=2026-12-20

date closing = received
  close day 20

date payment = closing
  day 10 of month +1

Related codes: E065, E129

E132

error — The divisor can be zero

When. A rounding function's argument divides by a value (half_up(total / units, 1JPY)), and the range of the divisor includes zero. The range is worked out by interval arithmetic from the inputs' ranges, the ranges of the derives and the defines, and what the tables above write — where a row writes one of its own table's columns, only the values its cell there lets through (| >=1 | units | writes units of one or more). A constraint is not read. A divisor that reads a value with no range cannot be shown never to be zero either, and is this too. Where an input that makes the divisor zero is found, the notes name it.

Fix. Narrow the divisor's range so that it leaves zero out (range >=1), divide by max(units, 1), or put a table that picks another value when it is zero (the two rows | 0 | 1 | and | >=1 | units |) and divide by that table's output. Dividing by b - a under constraint a < b, write max(b - a, 1) too: under the constraint it is the same value. What to answer when it is zero is a business decision, and rulec does not make it.

Smallest reproduction:

rule average_price v1

inputs
  total : money[JPY]  range >=0JPY <=10000JPY
  units : number  range >=0 <=100

outputs
  average : money[JPY]  round half_up(1JPY)

define per_unit : money[JPY] = half_up(total / units, 1JPY)

result average = per_unit

Related codes: E115, E108, E019

E139

error — The argument of isqrt can be below zero

When. The range of x in isqrt(x) includes values below zero. The range is worked out by intervals as for E132, and an argument that reads a value with no range is this too. A number below zero has no square root, and the generated code would have no answer on that call. Where an input that makes the argument negative is found, the notes name it.

Fix. Narrow the inputs' ranges so that the argument stays at zero or more, or pass isqrt(max(x, 0)). A power is written as a product: isqrt(n * n * n) is n to the power 1.5, rounded down.

Smallest reproduction:

rule root_of_gap v1

inputs
  gap : number  range >=-10 <=100

outputs
  root : number  round down(1)

define whole_root : number = isqrt(gap)

result root = whole_root

Related codes: E108, E103

E140

error — An output's range does not hold the values it takes

When. The range written on an output's line leaves out values the output can take. What it can take is what decides it — the cells of a table's rows, a define of its name, the expression of the result — under the inputs' ranges and the constraint lines, put out to the grid of its rounding. Where rulec cannot work that out (the output reads an input with no range, or is no number), the range cannot be checked, and is this too. An output's range is the range other languages (kaname, dandori, …) read the rule's answer in, so one too narrow would let their checks of ranges pass over values the rule really returns.

Fix. Widen the range to the values the message states (fix.text holds it, as range >=0USDc <=1000USDc). Where it cannot be checked, give the inputs the output reads a range, or take the output's range off: an output with none written has the range rulec works out, and the other languages read that.

Smallest reproduction:

rule fee v1

inputs
  amount : money[USDc]  range >=0USDc <=10_000USDc

outputs
  fee : money[USDc]  round down(1USDc)  range >=0USDc <=500USDc

result fee = amount × 10%

A range that cannot be checked:

rule fee v1

inputs
  amount : money[USDc]

outputs
  fee : money[USDc]  round down(1USDc)  range >=0USDc <=500USDc

result fee = amount × 10%

Related codes: E112, E061, E103

E134

error — A call answers a carried number outside its range

When. A call from a state a case can reach from initial answers a carried number outside its input's range: the next call cannot be passed it, and the case cannot go on from there. The shortest sequence of calls comes with it as witness.trace. A row that leaves the range only from states no case reaches is W126's.

Fix. Correct the row that decides the next value (stop at the bound, or move on to another state), or widen the input's range if the value does occur in the business.

Smallest reproduction:

rule t(t) v1

enum s(s) = run(run) | done(done)

inputs
  st(st)       : s
  again(again) : bool
  n(n)         : number  range >=0 <=3

outputs
  nx(nx) : s
  nn(nn) : number  round down(1)

define more(more) : number = n + 1

table m(m)
policy first
| st   | again | n   | -> nx(nx) : s | nn(nn) : number |
| run  | true  | <=2 | run           | more            |
| run  | true  | -   | done          | more            |
| run  | -     | -   | done          | n               |
| done | -     | -   | done          | n               |

machine k(k) over m
  carry   st -> nx
  carry   n -> nn
  initial run
  initial n 0

Related codes: E072, E135, W126

E135

error — An always line is broken

When. Some sequence of calls reaches a state the always line speaks of (those after when, or any state when there are none) with a carried value its cell does not accept. The shortest one comes with it, and every call in it is an input the rule takes and answers. A place where a carried number has left its range is E134's, and is not looked at here.

Fix. Correct the rows that decide where a case goes and what it carries on, or the always line if the claim is wrong as business.

Smallest reproduction:

rule t(t) v1

enum s(s) = run(run) | done(done)

inputs
  st(st) : s
  n(n)   : number  range >=0 <=3

outputs
  nx(nx) : s
  nn(nn) : number  round down(1)

define more(more) : number = n + 1

table m(m)
policy first
| st  | n  | -> nx(nx) : s | nn(nn) : number |
| run | <3 | run           | more            |
| -   | -  | done          | n               |

machine k(k) over m
  carry   st -> nx
  carry   n -> nn
  initial run
  initial n 0
  always  n <=2 when run

Related codes: E072, E126, E134

E136

error — The prerequisites go round in a cycle

When. Following what a value needs, and what that needs, leads back to the value. The values inside an or are followed too. One is given for each set of values that reach one another, with one way round from the first value of the set back to it, and the values of the set. Each value on a cycle waits for another on it, so the cycle cannot be entered anywhere; where another value of an or does lead in, a cycle is still where prerequisites are misread, and it is an error.

Fix. Drop one of the prerequisites on the cycle.

Smallest reproduction:

rule t(t) v1

enum k(k) = a(a) | b(b) | c(c)

inputs
  x(x) : k

outputs
  r(r) : bool

table j(j)
policy unique
| x | -> r(r) : bool |
| - | true           |

prerequisites p(p) over k
  start a
  b     requires c
  c     requires b

Related codes: E071, E137

E137

error — A value cannot be reached from start

When. Starting from the values of start and adding every value each group of whose needs has a value already held, some value is never added. With no cycle, what is never added waits on a value no case can take (requires nothing): one is given for each value with a group of such values alone, and the values that wait on it are listed in a note. A cycle is E136's and is not looked at here. A value written as one no case can take is not said: it says so itself.

Fix. Drop the prerequisite no case can take, or, if the value cannot be had either, write <value> requires nothing "<reason>".

Smallest reproduction:

rule t(t) v1

enum k(k) = a(a) | b(b) | c(c)

inputs
  x(x) : k

outputs
  r(r) : bool

table j(j)
policy unique
| x | -> r(r) : bool |
| - | true           |

prerequisites p(p) over k
  start a
  b     requires c
  c     requires nothing "retired"

Related codes: E071, E136

E138

error — A prerequisite comes after the value in the order of ordered by

When. In the table ordered by names, a value's answer comes before the answer of something it needs (a value inside an or too). A prerequisite no case can take, and one on a cycle, are E137's and E136's, and are not looked at here.

Fix. Correct the table's answer, or the prerequisite.

Smallest reproduction:

rule t(t) v1

enum k(k) = a(a) | b(b) | c(c)
enum s(s) = one(one) < two(two)

inputs
  x(x) : k

outputs
  r(r) : s

table j(j)
policy unique
| x    | -> r(r) : s |
| a, c | one         |
| b    | two         |

prerequisites p(p) over k
  start a
  b     requires a
  c     requires b
  ordered by j

Related codes: E071, E136

W125

warning — No sequence of calls reaches a state

When. The enum of the carried state has a value that no sequence of calls from initial reaches.

Fix. Add the transition into it, or drop it from the enum. If it is kept for cases moved over from another version, leave it: diff looks at the move.

Smallest reproduction:

rule t(t) v1

enum s(s) = a(a) | b(b) | c(c) | d(d)
enum e(e) = fwd(fwd) | rev(rev)

inputs
  st(st) : s
  ev(ev) : e

outputs
  nx(nx) : s

table m(m)
policy unique
| st | ev  | -> nx(nx) : s |
| a  | fwd | b             |
| a  | rev | a             |
| b  | fwd | c             |
| b  | rev | a             |
| c  | -   | c             |
| d  | -   | d             |

machine k(k) over m
  carry   st -> nx
  initial a
  final   c

Related codes: W126, E125

W126

warning — A transition is never taken from a state a case can reach

When. A row of the table that decides the transitions applies only in states no sequence of calls from initial reaches. It comes with W125. A row whose state a case does reach, but never with the value of a held input or of a carried value (a carry line after the first) the row needs, is this warning too — among them a row that would answer a carried number outside its range, from values no case holds.

Fix. Add the transition into the state it needs, or drop the row.

Smallest reproduction:

rule t(t) v1

enum s(s) = a(a) | b(b) | c(c) | d(d)
enum e(e) = fwd(fwd) | rev(rev)

inputs
  st(st) : s
  ev(ev) : e

outputs
  nx(nx) : s

table m(m)
policy unique
| st | ev  | -> nx(nx) : s |
| a  | fwd | b             |
| a  | rev | a             |
| b  | fwd | c             |
| b  | rev | a             |
| c  | -   | c             |
| d  | -   | d             |

machine k(k) over m
  carry   st -> nx
  initial a
  final   c

Related codes: W125, E102

W127

warning — A claim of the machine could not be settled

When. A claim turns on a cell for which no input was built and none was shown impossible. Derived values that share an input, with a solution only among the rationals, are what is left here — the place W114 is about. A machine that carries numbers (a carry line after the first) leaves three more: the numbers were walked in intervals, since every value one by one is past the budget, and a sequence that breaks the claim over the intervals could not be made again from calls that really happen; walked in intervals, that every state a case reaches can still get to a final one is not followed; or a carried number's next value moves with another input inside a cell, and the values between the ones the calls seen answered were not each tried. What was not settled is not said to hold.

Fix. If no input really falls in the cell, write the condition over whole numbers (a >= 3JPY for twice >= 5JPY). If one does, write it down as an example or a scenario. Where the carried numbers were walked in intervals, raising --budget or narrowing their ranges lets every value be walked one by one, and the claim settled.

Smallest reproduction:

rule t(t) v1

enum s(s) = p(p) | q(q)

inputs
  st(st) : s
  a(a) : money[JPY]  range >=0JPY <=100_000JPY

outputs
  nx(nx) : s

derive twice(d) : money[JPY] = a + a  range >=0JPY <=200_000JPY

define above(up) : bool = twice >= 5JPY
define below(dn) : bool = twice <= 5JPY

table m(m)
policy first
| st | above | below | -> nx(nx) : s |
| p  | true  | true  | q             |
| -  | -     | -     | st            |

machine k(k) over m
  carry   st -> nx
  initial p

Related codes: W114, E128

W128

warning — No input was found that reaches a row, and none was shown not to

When. A row that writes a cell on a computed column (a derive, a define): no input reaching it was found among the rule's examples, by the search the vectors are made with, among the combinations of the inputs' candidate values (up to 4,096), or as the input behind the gap the completeness check finds with the row taken out, and it could not be shown that no input does. The point of such a row is not an input, and whether one reaches it turns on how the expressions relate (a value only whole numbers reach, or one they never do). A row nothing decided is not passed in silence. A row of inputs and enums alone needs no search: its point is an input.

Fix. Look at the expressions and the row's conditions together. If some input reaches the row, write it as an example and the warning goes; if none does, delete the row. If deleting it brings a completeness gap (E101), rulec cannot show that the combination never comes either, and the row can stay.

Smallest reproduction:

rule square_band v1

inputs
  side : number  range >=0 <=10

outputs
  band : number  round down(1)

define area : number = side * side

table band_of
policy first
| area | -> band : number |
| 2    | 1                |
| -    | 2                |

Related codes: E101, E102, W114, W127

W130

warning — A prerequisite is already needed through another

When. One of a value's prerequisites is among what another of its prerequisites needs, or what that needs in turn: written or not, it leaves the value to be taken at the same times. The values inside an or are left out on both sides.

Fix. Drop it: one place less to correct when the prerequisites change.

Smallest reproduction:

rule t(t) v1

enum k(k) = a(a) | b(b) | c(c)

inputs
  x(x) : k

outputs
  r(r) : bool

table j(j)
policy unique
| x | -> r(r) : bool |
| - | true           |

prerequisites p(p) over k
  start a
  b     requires a
  c     requires b, a

Related codes: E136

W105

warning — Shadowing that needs review: an earlier row hides part of a later one

When. In a policy first table, two rows partially intersect and disagree on the output. Structural shadowing (the staircase) and equivalent shadowing are folded into a count line; only the pairs that need review are listed. A pair that meets only where no input reaches is not reported, as under E105.

Fix. If it is intended, leave it: check --diff-base in CI reports only newly created pairs. To let the later row win, move it above the earlier one. --show-shadow lists every pair.

Smallest reproduction:

rule t(t) v1

enum k(k) = a(a) | b(b)

inputs
  x(x) : k
  y(y) : bool

outputs
  r(r) : money[JPY, incl_tax]  round down(1JPY)

table j(j)
policy first
| x | y    | -> r(r) : money[JPY, incl_tax] |
| a | -    | 100JPY                         |
| - | true | 200JPY                         |
| - | -    | 300JPY                         |

Related codes: E105, W110, E102

W110

warning — A first table with no overlaps

When. The table is policy first, yet no two rows overlap. The order carries no meaning, and unique is the stronger guarantee.

Fix. Change it to policy unique. From then on, the check guarantees that reordering the rows cannot change the meaning.

Smallest reproduction:

rule t(t) v1

enum k(k) = a(a) | b(b)

inputs
  x(x) : k

outputs
  r(r) : bool

table j(j)
policy first
| x | -> r(r) : bool |
| a | true           |
| b | false          |

Related codes: W105, E105

W111

warning — A declaration is never used

When. An input, a derived value, a group or an enum value appears in no cell of any table. It can be the symptom of a forgotten column, or a legitimate contract. Values of an imported type are not checked this way, nor is a value of an ordered enum when every name of that type is an input whose range leaves the value out: no call brings it. Nor is a value a cell that hands an input on (| false | requested |) answers with, when no table tests the enum in a column: no table could give it a row of its own.

Fix. If the input serves only as a range check at the entry, mark the declaration contract_only. If an enum value is meant to fall through to the default row, mark the value default. Otherwise either add the column to a table, or delete the declaration.

Smallest reproduction:

rule t(t) v1

inputs
  x(x) : bool
  w(w) : mass[g]  range >=0g <=10kg

outputs
  r(r) : bool

table j(j)
policy unique
| x     | -> r(r) : bool |
| true  | true           |
| false | false          |

Related codes: E101, E012

W116

warning — No example uses this sequence

When. A sequence is written and no example names it. A sequence runs only when an example names it, so this one never runs.

Fix. Add the example that walks it, or drop the sequence. Written and unused is usually the trace of an example left unwritten.

Smallest reproduction:

rule t(t) v1

enum v(v) = a(a) | b(b)

elements xs(xs)
  k(k) : money[JPY, incl_tax]  range >=0JPY <=10JPY

outputs
  r(r) : money[JPY, incl_tax]  round down(1JPY)

table j(j)
policy unique
| k      | -> d(d) : v |
| <=5JPY | a           |
| >5JPY  | b           |

fold d over xs
  a -> next
  b -> take_first k
  empty -> 0JPY
  exhausted -> held

sequence s(s)
| k    |
| 3JPY |

Related codes: E027, W111

W119

warning — A pinned fragment is not cited

When. A pin line sits under a source line, but no @ in the rule cites that fragment. It is what remains after a citation was removed.

Fix. Remove the pin line; rulec source pin does.

Smallest reproduction:

rule t(t) v1

source 法 = law "000AC0000000001" asof 2026-04-01
  第1条 sha256:ce31217424a10206
  第2条 sha256:0000000000000000

inputs
  a(a) : bool

outputs
  x(x) : bool

table 表(t1)  @法 第1条
| a | -> x |
| - | true |

With sources/law/000AC0000000001@2026-04-01/MainProvision-Article_1.xml beside it:

<Article Num="1"><ArticleTitle>第一条</ArticleTitle><Paragraph Num="1"><ParagraphNum/><ParagraphSentence><Sentence>甲は、乙とする。</Sentence></ParagraphSentence></Paragraph></Article>

Related codes: E037

W120

warning — The copy states a value no row uses

When. A cell of the copy a table cites whole with @source table1 is nothing but a number, and no row uses that value. A dropped row does not show up in the completeness check: its inputs fall into one of the rows that remain. Only cells that are nothing but a number are asked about, so revised 1 April 2026 is not counted as an amount, and a row that merges what the copy lists — <=3kg over its 1kg, 2kg and 3kg — accounts for all of them.

Fix. Compare the table with the copy and check that no row was left out; if a revision added a row, transcribe it. If the table transcribes only part of the fragment — one origin of a tariff sheet that lists several — move the citation from the table line onto the end of each row that came from it. A row's citation says only where that row came from, so the rest goes unasked while the amounts are still held to the copy (E116).

Smallest reproduction:

rule t(t) v1

source tariff = file "tariff.md" sha256:583beb1ff0102e2e
  table1 sha256:07423c36f9e9259f

inputs
  a(a) : bool

outputs
  x(x) : money[JPY]  round down(1JPY)

table t1(t1)  @tariff table1
policy unique
| a | -> x   |
| - | 990JPY |

With tariff.md beside it:

# Tariff

| Destination | Fare |
|---|---|
| Kinki | 990JPY |
| Kanto | 880JPY |

With tariff.md.fragments/table1.tsv beside it:

Destination Fare
Kinki   990JPY
Kanto   880JPY

Related codes: E116, W119

W118

warning — No row of an applied table is reached in this apply

When. Every row of an applied table or clause is unreachable in this rule: what is bound never reaches its conditions, or other definitions of this rule (a clause with overrides apply:table, say) take precedence over all of it. Rows unreachable one by one draw no word: a callee's table is usually written for a wider range than this rule's, and doc lists those rows as unused by this apply.

Fix. If this apply does not need the table, leave it out with except <table>. If it should be used, look at the bindings and the with mapping.

Smallest reproduction:

rule t(t) v1

inputs
  n(n) : number  range >=1 <=10

outputs
  y(y) : number  round down(1)

apply call(c) = "callee.rule" sha256:1c554496c37f709f
  a = n
  x -> y

clause special(special) -> call:x
  when always
  then 3
  overrides call:t

With callee.rule beside it:

rule callee(callee) v1

inputs
  a(a) : number  range >=1 <=10

outputs
  x(x) : number  round down(1)

table t(t)
policy unique
      | a   | -> x |
small | <=5 | 1    |
large | >5  | 2    |

Related codes: E102, W117

W117

warning — An exception with no effect

When. No row of this table meets a row of what its overrides line names. Precedence decides which wins when an input matches both, so with no meeting rows the line decides nothing. It is the usual sign of a proviso transcribed so that it no longer carves out part of the main rule.

Fix. Compare with the source and fix the conditions of this table's rows or of the target's. If they really never meet, drop the overrides line.

Smallest reproduction:

rule t(t) v1

inputs
  a(a) : bool

outputs
  x(x) : bool

table base(base)
   | a     | -> x  |
r1 | true  | true  |
r2 | false | false |

table special(special)
overrides base:r1, base:r2
| a    | -> x  |
| true | false |

Related codes: E035, E105

W121

warning — An identifier collides, or cannot be used, in a target language

When. An ASCII alias is a keyword of one of the targets, or a name that language already uses. An alias becomes a function, a parameter, a type or a member there. The rule's alias also names a module or a package, and the standard library's own names are held against it there. Or a rule and an enum it declares take one type name: Go declares the rule's function and the enum's type in one package, and Java nests the enum in the class the rule is, so neither compiles (an enum read from an enums file is written by that file's package or class, and does not clash). It is raised too for a name with no alias that is a keyword: the generated code of every language then writes the name with _ after it (class_), and with a number after that where class_ is the identifier of another name of the rule (class_2). It is raised as well where two names the author wrote, whose identifiers (the alias, else the name) differ, are one identifier in the spelling of some target: the lowerCamelCase values are written in, the PascalCase of a struct's fields, the JSON name of a field of the .proto, the upper case of an enum's values, the _ put after a keyword or after the name of a variable a language keeps, a column's name read with case set aside (SQLite), and the first 63 bytes of a name (PostgreSQL). And where a target cannot take an identifier: a language that reads a name starting with a capital as a constant, a NamedTuple, which takes no field starting with _, PostgreSQL, which cuts a column's name longer than 63 bytes, the languages in which a name of _ alone is neither a variable nor a field, SQLite, which gives a column no name of true or false, and the .proto, which takes no name that is not in ASCII. And where a name the author wrote is one the generated code uses itself: the types of the rows that matched, of the answer and of one element, the errors, the helpers that round and the rest, its locals, the names the language has built in, the messages and the fields of the .proto (the trace of DecideResponse), the <TYPE>_UNSPECIFIED of an enum's values, the names the service of Connect imports, the package names a language's command keeps, and the name of the file a program starts in. The warning's notes say which targets. Two names every language spells as one (the types of two enums, say) stop the check with E034.

Fix. A keyword means the generated code for that language does not compile (type as an input's alias breaks Rust). A name that is taken means the rule's function or an enum's type hides it (sum as the rule's alias hides Python's builtin). A module's name means, with time as the rule's alias, that the module generated as time collides with the standard library's time; the warning's notes say where and how. A parameter or a local shadows nothing outside its own body, so there the warning is raised only for a keyword. Of two names that are one identifier in a target you generate, give one an alias; to an identifier a target cannot take, or a name its generated code uses itself, give an alias it takes (one that starts with neither a capital nor _, is 63 bytes or fewer, and is no name the generated code uses). For a target you do not generate, leave it.

Smallest reproduction:

rule t(t) v1

inputs
  kind(type) : number  range >=0 <=10

outputs
  o(o) : number  round down(1)

table j(j)
policy first
| kind | -> o(o) : number |
| >=5  | 1                |
| -    | 0                |

Two names Swift and Java spell alike:

rule flat_fee v1

inputs
  qty : number  range >=0 <=10

outputs
  fee : money[USD]  round down(1USD)

define base_fee : money[USD] = 3USD
define baseFee : money[USD] = 2USD

table fee_by_qty
policy unique
| qty | -> fee   |
| <=5 | base_fee |
| >5  | baseFee  |

Two names SQLite reads as one column:

rule small_fee v1

inputs
  qty : number  range >=0 <=10

outputs
  fee : money[USD]  round down(1USD)

define afee : money[USD] = 3USD
define aFee : money[USD] = 2USD

table fee_by_qty
policy unique
| qty | -> fee |
| <=5 | afee   |
| >5  | aFee   |

Two long names PostgreSQL cuts to one:

rule spring_fee v1

inputs
  qty : number  range >=0 <=10

outputs
  fee : money[USD]  round down(1USD)

define the_fee_for_a_small_order_placed_before_the_spring_campaign_of_2026_began : money[USD] = 3USD
define the_fee_for_a_small_order_placed_before_the_spring_campaign_of_2026_ended : money[USD] = 2USD

table fee_by_qty
policy unique
| qty | -> fee                                                                    |
| <=5 | the_fee_for_a_small_order_placed_before_the_spring_campaign_of_2026_began |
| >5  | the_fee_for_a_small_order_placed_before_the_spring_campaign_of_2026_ended |

A name Ruby reads as a constant:

rule base_fee v1

inputs
  qty : number  range >=0 <=10

outputs
  fee : money[USD]  round down(1USD)

define Base : money[USD] = 3USD

table fee_by_qty
policy unique
| qty | -> fee |
| <=5 | Base   |
| >5  | 2USD   |

A name the generated code uses itself:

rule fee_and_trace v1

inputs
  qty : number  range >=0 <=10

outputs
  fee   : money[USD]  round down(1USD)
  trace : number  round down(1)

table fee_by_qty
policy unique
| qty | -> fee | trace |
| <=5 | 3USD   | 1     |
| >5  | 2USD   | 2     |

A name of _ alone:

rule underscore_fee v1

inputs
  qty : number  range >=0 <=10

outputs
  fee : money[USD]  round down(1USD)

define _ : money[USD] = 3USD

table fee_by_qty
policy unique
| qty | -> fee |
| <=5 | _      |
| >5  | 2USD   |

Related codes: E009, E011, E034

W115

warning — No element can land on this verdict

When. A fold has an arm for a verdict no row produces. It is the other side of E024: not a hole but an arm nothing reaches, and more often the trace of a table that changed than of an arm written by mistake.

Fix. Look again at the table's rows, or drop the arm. Which of the two is right is decided by reading the table, not this message.

Smallest reproduction:

rule t(t) v1

enum v(v) = a(a) | b(b)

elements xs(xs)
  k(k) : money[JPY, incl_tax]  range >=0JPY <=10JPY

outputs
  r(r) : money[JPY, incl_tax]  round down(1JPY)

table j(j)
policy unique
| k | -> d(d) : v |
| - | a           |

fold d over xs
  a -> take_first k
  b -> next
  empty -> 0JPY
  exhausted -> held

Related codes: E024

W114

warning — Unconfirmed overlap: an input may match both rows

When. Two rows of a policy unique table may overlap, but no input producing that was constructed and infeasibility was not proven either. Derived values sharing an input and the thresholds inside a boolean define no longer fall here: the elimination decides them. What is left is what it cannot decide because it works over the rationals — twice above is always even and never exactly 5JPY, but 2.5JPY is a rational. A system past the cap of 400 inequalities and one that spans two units are the same: not proven, which is not the same as possible. A pair that meets only where the rows of a table above leave a derive no values does not fall here either: the check reads that and drops it.

Fix. If an order satisfying both conditions can exist, fix the rows: the outputs differ, so a match is a contradiction. If none can exist, leave it — the generated code carries a guard that returns an error rather than silently picking the earlier row.

Smallest reproduction:

rule t(t) v1

inputs
  a(a) : money[JPY]  range >=0JPY <=100_000JPY

outputs
  r(r) : bool

derive twice(d) : money[JPY] = a + a  range >=0JPY <=200_000JPY

define above(up) : bool = twice >= 5JPY
define below(dn) : bool = twice <= 5JPY

table j(j)
policy unique
| above | below | -> r(r) : bool |
| true  | -     | true           |
| -     | true  | false          |
| false | false | false          |

Related codes: E105, W105, E109

W901

warning — A key is written in the file

When. Somewhere in the rule, in a string or a comment alike, there is a value in the shape of a key: an AWS access key ID, a key or token of GitHub, Slack, Stripe, OpenAI, Anthropic or Google, a Slack incoming webhook URL, or a PEM private key, each a shape its provider fixes. Every language of ritsu looks for them the same way. The diagnostic gives the kind of key, its prefix and its length, and never the key nor its line.

Fix. Keep the key where the code runs (an environment variable, the platform's connection or secret store) and read it from there. If it is real, revoke it with its provider first: taking it out of the file leaves it in the history of the repository. If it is a value for tests, write ritsu: test secret in a comment on the same line.

Smallest reproduction:

rule seats v1
# the Google Sheets API key the counts were read with: AIzaSyD-ritsu-fake-key-for-tests-000000

enum kind = workshop | talk

inputs
  event_kind : kind

outputs
  needed : number  round down(1)

table pick
policy unique
| event_kind | -> needed : number |
| workshop   | 30                 |
| talk       | 80                 |

E074

error — A from sum, from max or from min, or an elements … from, is misshapen

When. A from sum, from max or from min has no of <field of an element>; a from max or from min has no empty <value>, or its empty value is not a value of the input's type, or lies outside the input's range; or of or empty is written on a from that takes none (only max and min need empty: a sum of none is 0, and a count of none is 0). Also: something other than from <shape>.<collection> follows the name of an elements line; a field of a sequence taken from a contract has no from; a field of a sequence not taken from one has one; or the from of a field of an element is not a path inside the element.

Fix. Correct the shape: from max order.lines of amount empty 0JPY. empty is the input's value when no element is looked at, and what it is is a business decision. To take a sequence from the contract, write elements lines from order.lines, and on each field the path it is read from in an element, such as from qty. If the caller builds the sequence and passes it, take the from off its fields.

Smallest reproduction:

rule t(t) v1

shape order(order) = jsonschema "order.json" "#/$defs/Order"

inputs
  top(top) : money[JPY]  range >=0JPY <=1000JPY  from max order.lines of amount

outputs
  x(x) : bool

table t1(t1)
policy unique
| top | -> x |
| -   | true |

With order.json beside it:

{"$defs":{"Order":{"type":"object","required":["lines"],"properties":{"lines":{"type":"array","maxItems":10,"items":{"type":"object","required":["amount"],"properties":{"amount":{"type":"integer","minimum":0,"maximum":1000}}}}}}}}

Related codes: E120, E121, E122, E022

E070

error — A random input or a claim of chances is not shaped right

When. An input marked random cannot be drawn: a number without both ends of its range, an input that may be absent (?), a date or a string, an input read out of the caller's object with from (what from sum and the like read is the caller's value, not a draw, whether random is written before the from or after it), or an input a machine carries. Or random is written on an output or on a field of an element. Or chances is misshapen: something follows chances on its line, there is no table under it, the header has no ->, its last column is not chance, it names no output, or there are no rows; an input that is not random has no column, a random input or a name that is not an input is on the left, a name that is not an output is on the right, a sequence's column in a rule that walks holds something other than the name of a sequence, or a chance cell cannot be read as a rational, a rate or comparisons with one, between 0 and 1. A chances table in a rule with no random input is this too.

Fix. Give a number that is drawn a range with both ends, as in range >=0 <=13, and draw a number, an enum or a truth value. The header of chances names every input that is not random, then ->, the outputs the claims are about, and chance last. A column that takes any value gets -, and a chance is written like 5/14, 35% or <=1/100.

Smallest reproduction:

rule t(t) v1

inputs
  roll(roll) : number  random

outputs
  r(r) : bool

table j(j)
policy unique
| roll | -> r(r) : bool |
| <5   | true           |
| >=5  | false          |

Related codes: E073, E133, E108

E073

error — A pick by weight is not shaped right

When. A take_weighted has no by; there are two draw lines; or a fold with no take_weighted has a draw line. Or a rule has two folds that pick by weight; such a fold has an arm other than next and take_weighted; a weight is not a number, or can be negative; there is no draw line; or what draw names is not an input marked random that is a number whose range starts at 0.

Fix. Write the arm take_weighted <value> by <weight> and one line draw -> <random input>, and make the input it names a random number with range >=0 <=<top>. A weight is a whole number, 0 or more. Split the rule in two if it needs two picks.

Smallest reproduction:

rule t(t) v1

enum v(v) = skip(skip) | pick(pick)

elements os(os)
  ix(ix) : number  range >=1 <=9
  ok(ok) : bool
  w(w)   : number  range >=0 <=10

inputs
  roll(roll) : number  range >=0 <=99  random

outputs
  p(p) : number  round down(1)

table j(j)
policy unique
| ok    | -> v(v) : v |
| true  | pick        |
| false | skip        |

fold v over os
  skip      -> next
  pick      -> take_weighted ix by w
  empty     -> 0
  exhausted -> held

Related codes: E021, E070

E133

error — A claim of chances does not hold

When. The chance a row of chances states is not the chance, with the random inputs drawn uniformly, at some combination of the inputs that are not random its cells let in (or does not pass the comparisons it states). The combination comes with it, and the chance there: an exact rational, from counting the points of the random inputs.

Fix. If the table is right, correct the claim; if the claim is the business truth, correct the table's rows.

Smallest reproduction:

rule t(t) v1

inputs
  roll(roll) : number  range >=0 <=9  random

outputs
  r(r) : bool

table j(j)
policy unique
| roll | -> r(r) : bool |
| <3   | true           |
| >=3  | false          |

chances
| -> r | chance |
| true | 1/3    |

Related codes: E070, W129, E107

W129

warning — A claim of chances could not be decided within the budget

When. Where an answer moves inside a box with the random inputs, or the random inputs are read together with the others, the points that have to be run one by one go past the budget of one claim (one 50th of --budget's nodes, a million points by default). A sequence that cannot be read, or a point where the rule contradicts itself, leaves the claim undecided too. A claim that is not decided is said to be neither holding nor broken.

Fix. Narrow the cells of the inputs that are not random, or raise --budget.

Smallest reproduction:

rule t(t) v1

inputs
  roll(roll) : number  range >=0 <=999  random
  a(a)       : number  range >=0 <=9999

outputs
  r(r) : bool

derive d(d) : number = roll - a  range >=-9999 <=999

table j(j)
policy unique
| d   | -> r(r) : bool |
| <0  | true           |
| >=0 | false          |

chances
| a | -> r | chance |
| - | true | >=0    |

Related codes: E133, E109

W131

warning — The chances function runs the rule many times on every call

When. The points of the random inputs, multiplied together, are more than a million, and the function generated to count the answers runs the rule that many times on every call. A rule that picks by weight, its random input read by the draw line alone, is counted from its weights and does not get it.

Fix. If it is called often, narrow the ranges of the random inputs; in a rule that picks by weight, let only the draw line read the random input.

Smallest reproduction:

rule t(t) v1

inputs
  a(a) : number  range >=0 <=999  random
  b(b) : number  range >=0 <=9999  random

outputs
  r(r) : bool

table j(j)
policy unique
| a     | b | -> r(r) : bool |
| <500  | - | true           |
| >=500 | - | false          |

Related codes: W129, E108

E067

error — A contribution is not shaped as one

When. A file headed contribution cannot be read as one. Its to line is missing or written twice, or the file it names cannot be read or does not pass check. An extend adds a value to an enum not marked open, to an enum another enums file owns, a value the enum has, or a value with no after to an ordered enum. An into names something that is not an open value, or a term that is declared nowhere or is decided below the open value. A table or clause decides an output no definition opens, or one of an open output reads a name below the output's last definition. Lines are added to a prerequisites block not marked open, or a line of one of the block's own values. The after lines go round, two contributions share a name, the file holds a line a contribution cannot (outputs, result, apply, a machine, a sequence it walks, enums, imports, sources, contracts, constraints, claims of chances), or an input is drawn at random.

Fix. A contribution adds only to the places the file it adds to opens with open. Where it does not, ask its author to open it, or read it with apply and write a rule of your own. The notes say where it can add.

Smallest reproduction:

contribution c(c) v1
to "base.rule" signature:9e889c8a59016488

extend kind with z

With base.rule beside it:

rule base(base) v1

enum kind(kind) = a(a) | b(b)

inputs
  k(k) : kind

outputs
  fee(fee) : number  round down(1)

table t(t)
policy unique
| k | -> fee : number |
| a | 1               |
| b | 2               |

Related codes: E068, E040, E033, E131

E068

error — An open value is not shaped as one

When. An open line has no way of combining (combine sum, product, max or min), no range, or no empty for max and min; its type is not one the way of combining puts together (a sum takes numbers, money, quantities and rates, a product numbers and rates, max and min those and temperatures and sound levels); it is optional; or a term is on a finer step than the value. The range is the promise that the value stays inside it whatever is added, and the tables below are checked over the whole of it.

Fix. Write it open <name> : <type> combine sum|product|max|min [empty <value>] range >=… <=…. empty is the answer when there is no term, and it takes part as the smallest answer for max (the largest for min). Round a finer term to the value's step before adding it.

Smallest reproduction:

rule t(t) v1

inputs
  member(member) : bool

outputs
  bonus(bonus) : rate[step 1%]  round down(1%)

table p(p)
policy unique
| member | -> from_member : rate[step 1%] |
| true   | 10%                            |
| false  | 0%                             |

open bonus : rate[step 1%]  combine sum
  from_member

Related codes: E067, E131

E131

error — A value put together leaves the range it declares

When. The interval of an open value's terms — the rule's and those contributions add — put together as its combine says, leaves the range on the open line. The notes give each term's interval, the contribution each comes from, and an input that takes the value out of the range. The range is the promise that the value stays inside it whatever is added, and the tables below are checked over the whole of it; a value outside it would get an answer nothing checked.

Fix. Widen the range (the tables below are then checked over the wider range), or narrow what the contribution adds. Which is a business decision, so there is no fix.text.

Smallest reproduction:

contribution c(c) v1
to "base.rule" signature:b10f6ca4271b3f2f

inputs
  promo(promo) : bool

table q(q)
policy unique
| promo | -> from_promo : rate[step 1%] |
| true  | 15%                           |
| false | 0%                            |

into bonus
  from_promo

With base.rule beside it:

rule base(base) v1

inputs
  member(member) : bool

outputs
  bonus(bonus) : rate[step 1%]  round down(1%)

table p(p)
policy unique
| member | -> from_member : rate[step 1%] |
| true   | 10%                            |
| false  | 0%                             |

open bonus : rate[step 1%]  combine sum  range >=0% <=20%
  from_member

Related codes: E068, E112, E067

W132

warning — A term of an open value changes nothing

When. A term of an open value cannot move the value whatever it takes: in a max, every value of the term is at or below the empty value; in a min, at or above it; in a sum, it is always 0; in a product, always 1. The empty value of max and min is the value the terms start from, and it always takes part, not only when there is no term, so a cap written as a max leaves its terms with nothing to do, in silence. It is said of a term a contribution adds and of a term of the rule alike.

Fix. If a term of a max sets a cap, put the value together with min (and with max if a term of a min sets a floor). If the term can pass the empty value — above it for a max, below it for a min — write the values that do. If it is not needed, take it out.

Smallest reproduction:

rule t(t) v1

inputs
  plan(plan) : bool

outputs
  limit(limit) : number  round down(1)

table cap_of(cap_of)
policy unique
| plan  | -> plan_cap : number |
| true  | 40                   |
| false | 60                   |

open cap : number  combine max  empty 100  range >=0 <=100
  plan_cap

define limit : number = cap

Related codes: E068, E131