Validation Rules
The transpiler reports diagnostics with short codes. Errors stop code
generation (exit code 1); warnings do not. Run
cambrian-transpiler <file.cam> --check (or --project p.yaml --check) to
see them without writing output; --diagnostic-format json gives
machine-readable output. The authoritative source is
cambrian-transpiler/src/validate/. This page lists the codes that apply to
the public targets (--target evm, --target lean).
Families
| Prefix | Concern |
|---|---|
| V | Entity / route / member / phase / deploy / match |
| E | EVM / Solidity compatibility |
| L | Lean target |
| T | test / property / fuzz |
| I | invariant |
| F | project.yaml / imports |
| W | Warnings / lints |
Unless marked (warning), a code is an error. Codes tagged
EVM fire for both --target evm and --target lean (both model the EVM);
Solidity codes fire only for --target evm; Lean codes only for
--target lean.
V — entities, routes, members
| Code | Summary |
|---|---|
| V1 | Member transform in <route> names an unknown route |
| V2 | (warning) Route has no member transforms and an empty body |
| V3 | Unknown ^member, or a temporal cycle between members in one route |
| V4 | pure fn or library function reads an entity member, msg::*, sys::*, temporals, macros or impure evm::* (pass the value as a parameter) |
| V7 | Constant value is not a literal |
| V8 | Undefined identifier, function, macro or temporal reference |
| V9 | view route has member transforms, or (transitively) calls a route that has member transforms or state-changing actions |
| V10 | pure route has member transforms |
| V11 | pure route uses an impure construct (msg::*, sys::*, members, sends, deploys, call, …) |
| V12 | More than one init route, or an init route with a return type |
| V13 | Duplicate phase name |
| V14 | Unphased transform on a phased route, or phased transform on an unphased route |
| V15 | Transform references an unknown phase |
| V16 / V17 | identity member declares transforms / a default value |
| V20 | Empty string "" compared with an address |
| V22 | ns::name(...) without the matching use import — often a send misspelt as Entity::route(args) ~> dest (write route(args) ~> dest) |
| V23 | Named send or var capture to an untyped address (type the destination Address<E> or declare an extern entity), or to a route the target entity doesn’t declare |
| V24 | var x = r(...) ~> dest targets a route with no return type |
| V25 | var shadows a parameter or member, or is defined twice |
| V26 | var used in a member transform of the same or an earlier phase (or an unphased route) |
| V27 | Route-level where references a var defined in the body |
| V28 | Per-phase where references a var from the same or a later phase |
| V29 | Forbidden action inside an action-level for (var capture, return) |
| V30 / V31 | Duplicate extern entity (or clash with an entity) / duplicate extern route |
| V32 | deploy E(args) arity ≠ identity members + init-route params |
| V33 | EVM — from E(args) arity ≠ identity-member count of E |
| V34 / V35 | emit of an undeclared event / wrong arity or types |
| V36 | EVM — event with more than three indexed parameters |
| V38 / V39 | throw Name of an undeclared error / wrong arity or types |
| V40 / V41 | EVM — receive / fallback wrong shape (no params, no return, not view/pure; receive needs accept) / declared twice |
| V42 | return(value) in a route without -> T (bare return is fine) |
| V43 | ^member outside a member transform body |
| V45 | Several deploy of the same entity in one route: warning when arguments differ, error when two are identical (address collision) |
| V47 | (warning) Unreachable match arm: after _ / a bare identifier, a duplicate arm, or a catch-all after arms that already cover every case (Lean drops that catch-all). Also an error for duplicate names inside a library |
| V48 | if without else where a value is required |
| V49 | Bare stdlib call (min, pow, sha256, …) without the std::math:: / std::str:: / std::crypto:: prefix |
| V50 | Transform assigns an incompatible type (string ↔ number needs std::str::parse_* / format; address vs Address<E>) |
| V51 | Unused let in a route or pure fn body |
| V52 | Record cycle without Option / Vec / HashMap indirection, or a type alias that never unfolds |
| V53 | Nested pattern in let some(<pat>) (use an identifier or _) |
| V54 | Route body mixes phase blocks and bare actions |
| V55 | using method name collides with a built-in method of the receiver type |
| V56 | using names an undeclared pure fn or library |
| V57 | using function’s first parameter type doesn’t match the for type |
| V58 | Invariant deploy { … } must contain exactly one address |
| V59 | deploy { … } on a multi-entity invariant |
| V60 | Several from alternatives with different : throw annotations |
| V62 | EVM — init/constructor route reads msg::sender (under factory deploy it is the factory); pass the recipient as a constructor parameter. Warning in single-file mode, error with --project |
| V63 | EVM — init/constructor route lacks #[factory_only]. Warning in single-file mode, error with --project |
| V64 | #[factory_only] on a route other than the init/constructor route |
| V65 | Arithmetic (+ - * / %, wrapping variants) on an Option<_> operand |
| V66 | Non-exhaustive match without a _ / binding arm: a missing enum variant, some / none, true / false, or integer-literal arms with no catch-all |
| V67 | Route or pure fn with -> T never returns a value (and never throws), or returns a value of a different category (numeric / String / bool) |
| V68 | Record literal R { … } sets a field twice, names an unknown field, or omits a field (use base { field: v } to update an existing value) |
| V69 | Signed and unsigned operands with no common signed type: arithmetic mixing a signed operand with u128 / U256, or a comparison with U256; cast one side with as |
| V70 | Member transform in r(a, b) binds a different number of parameters than route r declares; bind all (_ for unused ones) or none (in r()) |
E — EVM / Solidity
| Code | Summary |
|---|---|
| E05 | (warning) accept on a regular route has no EVM meaning (payability is inferred); accept receive() is exempt |
| E07 | Solidity — error for a closure as a value or a bare range outside for; warning for an unsupported iterator chain inside for / .fold |
| E08 | (warning) Solidity — enum variant with payload lowers to a tagged-union struct |
| E09 | (warning) EVM — from lowers to a msg.sender comparison |
| E16 | EVM — namespace call with no EVM lowering (including an unknown evm:: intrinsic name or msg:: field), or encode<…>(…) outside tests |
| E17 | Solidity — unrecognised HashMap transform shape; use m.insert / m.update / m.remove (optionally inside if / block / let) |
| E18 | Solidity — tuple type outside a multi-return position; use a record |
| E19 | Solidity — generic type other than Vec / HashMap / Option |
| E20 | Solidity — type name that is not a primitive, record, enum or alias |
| E21 | Solidity — unsupported as target (allowed: u8–U256, i8–i128, bool, address, pubkey) |
| E22 | EVM — typed send to an entity that is neither in the program nor an extern entity |
| E23 | Solidity — mixed-type tuple destructure whose slot types can’t be inferred |
| E25 | EVM — std::* call outside the standard-library allowlist |
| E27 | Reported after codegen (not by --check): an expression had no EVM lowering and was replaced by 0 in the generated Solidity. The generated files stay on disk for inspection, but the exit code is 1 |
| E28 | EVM — method with no Solidity or Lean lowering: Option .unwrap() / .unwrap_or() / .expect() / .is_some() / .is_none(), collection .get() / .set(). Use match / let some(x) = …, m[k], or a transform |
| E29 | Solidity — view route body contains a send, value transfer, deploy or emit (Lean accepts them) |
L — Lean
| Code | Summary |
|---|---|
| L1 | Generic type other than Vec / HashMap / Option |
| L2 | Closure as a value, or unsupported fold accumulator |
| L5 | expect throw N on a route that can never fail (vacuous theorem) |
| L6 | expect return … on a route without a return type |
| L7 | (warning) skip from is not honoured on Lean; from checks stay |
| L8 | Typed send whose destination doesn’t statically resolve to an in-program entity |
| L9 | var capture of a failing self-call from a route that can’t fail itself (add where / throw) |
| L10 | (warning) Invariant action route contains a send; Lean models the route as one atomic step |
| L11 | Fire-and-forget self-send to a failing route from a route that can’t fail |
| L13 | std::crypto::* (e.g. sha256) has no Lean lowering |
| L14 | Type name that is not a primitive, record, enum or alias |
| L15 | Unrecognised HashMap transform shape (Lean mirror of E17) |
| L16 | Namespaced call or msg:: / sys:: field with no Lean lowering (e.g. std::math::muldivmod, an unknown evm:: name) |
T — tests, properties, fuzz
| Code | Summary |
|---|---|
| T1 | for Entity names an unknown entity |
| T2 / T3 | call of an unknown route / wrong arity |
| T4 | with { … } / expect state { … } names a non-member field |
| T5 | expect before any call |
| T6 | Test with no call |
| T8 | (warning) Unknown context-block namespace (known: msg, sys) |
| T10 | Property parameter collides with a member, or a let shadows a parameter |
| T11 | bound in a property body, bad instance binding, or bound / assume in a plain test |
| T12 | assume expression is not boolean, or used in a plain test |
| T15 | skip if outside an invariant action body |
| T16 | advanceTime(...) outside an invariant action body (needs #[with_time]) |
| T17 | Instance binding shape: test needs p: value, fuzz needs p in lo..hi |
| T18 | test instance doesn’t bind every property parameter |
| T19 | field: * names a non-member |
| T20 | Unknown name in ctx { … } (allowed: msg::{sender,value}, sys::{now,timestamp,chainid,block_number,balance}) |
| T21 | Field both pinned and * in the same with { … } |
| T22 | msg:: / sys:: field in with { … } / init { … } instead of ctx { … } |
| T23 | Context parameter declared twice in one ctx { … } |
| T24 | Test deploy b = E(...) names an unknown entity |
| T25 | Test deploy binding name already in use |
| T26 | Test deploy constructor arity mismatch |
| T27 | Test deploy initial state names a non-member |
| T28 | call b.route(...) on a name not bound by an earlier deploy |
| T29 | call b.route(...): unknown route or wrong arity |
| T30 | Duplicate test let, let shadowing a parameter, or let x: T with a non-assignable initializer |
| T31 | Empty fuzz range for a parameter |
| T36 | expect emit in a property (only allowed in test) |
| T38 | Boolean expect that isn’t boolean-shaped, or mentions result after a route with no return type / when result is already a name in scope |
| T39 | Solidity — expect state on a member with no public Solidity getter (a Vec, a record holding a Vec / HashMap, or a map to Vec); assert through a view route and expect return |
I — invariants
| Code | Summary |
|---|---|
| I1 | action names an unknown route |
| I2 | action parameters don’t match the route (arity error; type mismatch is a warning) |
| I3 | check is not boolean |
| I4 | Forbidden statement in an action body (only bound / assume / skip if / advanceTime) |
| I5 | Invariant needs at least one action and one check |
| I6 | deploy { … } / senders { … } entry is not an address literal or constant |
| I8 | Duplicate instance name |
| I9 | Unknown instance entity, or action inst.route on an undeclared instance |
| I10 | init inst { … } names a non-member |
| I11 | Unqualified member in a multi-entity invariant (write inst.member) |
| I12 | derived body contains something other than let |
| I13 | Duplicate track name |
| I14 | exclude selectors { … } names an undeclared action |
| I15 | trace::* names an undeclared action or unknown accessor (length / count / lastWas) |
| I16 | trace::* outside an invariant assume / check |
| I17 | Solidity — check expression can’t be lowered for Foundry; move the logic into a view route |
| I18 | EVM — multi-instance invariant: several instances of an entity without identity members, duplicate identity values, or a cyclic deploy order |
F — project configuration and imports
| Code | Summary |
|---|---|
| F2 | Unknown lean.numerics value (expected overflow-wrap, overflow-panic, nat) or lean.intrinsics value (expected opaque, executable) |
| F4 | Imported file (import "…" or yaml imports:) declares entity / test / fuzz / invariant — list it under sources: |
| F5 | yaml imports: path or bare import "…" not found next to the importer or in any library_paths root |
| F6 | target: evm project that disables the CREATE2 factory deploy (on EVM, entities are always deployed via CREATE2 through the generated CambrianFactory) |
| F7 | (warning) Unknown project.yaml key (ignored), or fuzz.shrink (accepted, no effect) |
Loader failures use named codes instead: PKG_IO (missing file),
PKG_YAML (malformed YAML, missing target / sources, unknown target),
PKG_MERGE (conflicting declarations across files), and
PARSE_UNEXPECTED_TOKEN (syntax errors).
W — warnings
| Code | Summary |
|---|---|
| W1 | Member has no transforms |
| W2 | pure fn is never called |
| W3 | Entity constant is never used |
| W4 | Duplicate use import |
| W5 | deploy of an entity not defined in the program |
| W7 | Fuzz parameter type uses a fallback strategy on this target |
| W8 | test instance leaves a field: * unpinned (falls back to the default); also a file imported twice |
| W9 | Entity-local record / enum / type shadows a program-scope one (they are distinct types) |
| W10 | EVM — route updates a balance/allowance-like member without an emit |
| W11 | In a test, call of the init route is followed by msg { sender: … }; set the sender first |
| W13 | Invariant bootstraps the constructor without an explicit deploy { … } |
| W14 | Invariant deploy address is also listed in senders |
| W15 | Invariant check pins member == const while an action can decrease that member (likely vacuous) |