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 A rule cannot have both a fold and a count
E032 error The declared enum and the imported one disagree
E033 error A value of an imported enum has neither a row nor default
E034 error A row label 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 The callee differs from its pinned digest
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
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
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
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 alias collides with a word 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

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 / sequence / result / examples / policy / overrides / clause / source / apply / shape / machine / scenario). 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 / sequence / result / examples / policy / overrides / clause / source / apply / shape / machine / scenario. 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 or an output 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). 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           |

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).

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: either there is no comparison, or a side is not a single name.

Fix. Write constraint <input> <= <input>; the comparisons are <=, <, >= and >. 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.

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 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 there are two of them. A rule walks one sequence, and the fields of one of its elements are declared there.

Fix. Write elements fee_rows(fee_rows), and the fields of one element under it, declared the way inputs are. Two sequences mean two rules.

Smallest reproduction:

rule t(t) v1

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

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

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

outputs
  r(r) : bool

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

Related codes: E021

E021

error — The fold is not written correctly

When. The heading is not fold <verdict column> over <sequence>, or an arm is not one of next, stop, stop with <value>, take_unique <value>, take_first <value>, keep_max <value> by <key>, or the column being folded is not an enum.

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.

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>. 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.

Fix. Add empty -> <value>. 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

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, 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.

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>.

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 — A rule cannot have both a fold and a count

When. One rule has both. They are two endings for the same walk, and a fold can stop partway: what a count means on a walk that stopped is not decided.

Fix. To count, drop the fold and let a table judge the count. To fold, drop the count.

Smallest reproduction:

rule t(t) v1

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

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

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

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

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

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

Related codes: E021, E029

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 of an imported enum has neither a row nor default

When. A value of an imported enum appears in no row and is not marked default. For a value you wrote yourself that is a forgotten line (W111); for a value that came through the contract 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. 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;
}

Related codes: E032, W111, E101

E034

error — A row label appears twice

When. Two rows of one table carry the same label before their first |. A label is how an overrides line, a record's trace and a later version name the row, so it is unique within its table.

Fix. Change one of the two labels.

Smallest reproduction:

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

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 — The callee differs from its pinned digest

When. The apply heading carries no sha256:…, or the digest it carries differs from the digest of the callee file beside the rule. When the callee is amended, the answers of the rule that applies it change too; without a pin, that 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.

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    |

Related codes: E037, E038, E044

E041

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

When. A callee input is left unbound, 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, 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.

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'".

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. The callee's completeness was proved over that range and constraint; outside them there is no definition. 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 a constraint, declare the same relation with constraint.

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    |

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), itself has an apply, walks a sequence (elements, fold, count), or has a table or clause that produces one of its own enums. A broken rule expanded is a broken rule, and a provision applied through another is written flattened to one level.

Fix. Fix the callee first. To apply a rule that itself applies another or walks a sequence, write its expansion into this rule.

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    |

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 — an input, an output, a derive or a count — holds a word that belongs to none of range, round and contract_only. 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, or what is left of a bound whose unit did not lex as one. 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>).

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 written twice; a line under machine is not carry, initial, final, never or once; 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 two states together, make the pair one enum.

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 are not of one enum, 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 the cell is -. A state is answered again on every call that stays in it, so counting it with once breaks on the first stay. 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; it has a column for the carried input; or it lacks a column for another input. The first call starts from the initial state and every later one from the state the call before it answered, so the 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, the carried input, 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

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.

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. Every input the row would take is already taken by an earlier row, or the row names a value that the upstream table never produces. The two forms are told apart in the wording.

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.

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          |

Related codes: E101, W105, W110

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.

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.

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

Related codes: E108, E112

E104

error — A numeric output declares no rounding

When. A quantity, money or rate output has no round. 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 is not reported, which is the reading that also lets E102 call such a row dead.

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.

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.

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.

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.

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                           |

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. 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.

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.

Fix. If the divisor is business data, 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>) and the five rounding modes (down(x, 1JPY) and the rest).

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 and count yields a number. 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 or count would walk something that is not a collection, and when the value of a where does not fit the field. 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. Three shapes of it: the first word is not the name of a shape, a field along the way is not there, or the field a where tests is not a field of an element. The message says how far it resolved and which names were there. 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.

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"}}}}}}}}

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 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), and JSON Schema's required. 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.

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

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, 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

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.

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. 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.

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

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.

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.

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 alias collides with a word 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.

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. 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                |

Related codes: E009, E011

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.

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                 |