Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Fix a contract refusal

How-to · Level 2: Building real applications

Needs first: the contract walkthrough · Size an image from its contract

Use this page when nros build refuses a contract, or builds the image without sizing it from the contract, and you need to know which statement is wrong.

The contract is a statement about the code, made beside it. The build checks it at several points, and each check refuses rather than guesses, because a pool sized from a wrong statement fails at boot on a board that may have no console. Find the message you have below; each section names the fix. In the messages, angle brackets stand for your own names.

Before you start

  • The full output of the nros build <image> that failed.
  • The contract it read: <bringup>/launch/<stem>.contract.yaml, or <bringup>/system.contract.yaml for a bringup with no launch/.

Steps

  1. Rebuild the image and keep the output.

    nros build <image>
    
  2. Find the first refusal in the output and match it to a section below. Fix the contract or the code, then rebuild.

The contract does not parse

The contract grammar refuses any key it does not list:

at '<path>': unknown key in `qos` — did you mean `depth`? An unrecognised key used to be discarded in silence, which deleted whatever it was meant to declare

Correct the spelling; the contract format reference lists every key. An endpoint key holding a / is refused the same way:

endpoint key `/chatter` contains `/`: an endpoint key is a local name, and the node's FQN is prefixed to it (`/chatter` would become `<node>//chatter`). Use the endpoint's local name; remap the topic in the launch file or wire it under `topics:`

Key the endpoint by its local name (chatter) and put the absolute name under topics:. Once a contract has been refused, later commands refuse the model it left behind until a resolve succeeds:

SystemModel `<model>` was refused by its producer and cannot be trusted (marker `<marker>`):
  check: <check>
  input: <input>
  Fix the input and run `nros configure`; the next successful resolve removes the marker.

Fix the contract and run nros configure (or nros build) again.

The contract and a qos_overrides parameter disagree

<n> QoS statement(s) disagree between the launch contract and the `qos_overrides.*` parameters.

Each disagreement that follows names the topic, the parameter and its node, and the contract row, or says the row states no such policy. Make the contract state the value the parameter sets. The parameter configures the runtime and the contract sizes the buffers, so the two must agree.

The image was built with its configured defaults

The resolve line says the contract sized nothing:

nros build:   resolved → <workspace>/build/<bringup>__<image>/resolved.toml (entity counts not derived; configured defaults kept)

The reason is the refused line under [provenance] in that resolved.toml. The most common one:

<n> of <m> components in this image declare no entities:
    <pkg>::<component> (<class>)
Deriving over only the components that did would publish a slot count smaller than the image needs, and a short NROS_EXECUTOR_MAX_CBS fails entity creation at boot.

Sizing is all or nothing per image. Declare every listed component under nodes:, keyed by its node name. If the reason begins the launch tree resolved, and it declares neither wiring nor parameters, the build found no contract at all: check its file name and where it sits.

An endpoint is not matched

On a Rust image the build prints one line per endpoint it could not match to the contract, for example:

nros build:   subscription `chatter`: this subscription registers on `chatter`, which is not an absolute name. A contract states RESOLVED names, and resolving `chatter` needs the node's namespace AND its remappings -- neither of which reaches this reader per endpoint. Write the name absolutely at the call site, or declare the endpoint the contract already names

Write the name absolutely at the call site ("/chatter"). A node the launch file remaps is refused too (the launch description applies name REMAPPINGS to node): drop the remap, or accept that the endpoint keeps its worst-case sizes. A line beginning no subscription in this image's contract carries type names what the contract does describe; correct the type or the topic.

The census check refuses a cross image

Before it builds an image for a board other than the host, nros build runs the host image generated from the same launch file once, records what every component creates, and compares that census with the contract, row by row:

error missing-in-contract <node> sub /chatter
    the code creates this subscription on `/chatter`; no `sub:` row of `nodes.<node>` is wired to it, so no `topics:` entry names it either
    in <contract>, add under `nodes.<node>.sub` (the alias is the author's; `chatter` is this name's last segment):
        chatter: { qos: { depth: <depth> } }
    and wire it under `topics`:
        /chatter:
          type: <package>/msg/<Name>
          sub: [<node>/chatter]
    not waivable: every pool derives one short; the first catch is ExecutorFull on a board with no console
census check: <n> confirmed, <n> error(s), <n> warning(s), <n> waived

Each row gives the edit. The verdicts:

VerdictMeansFix
missing-in-contractthe code creates it, the contract does not declare itadd the row the message prints
phantomthe contract declares and wires it, the code never creates itremove the row, or waive it
unwireddeclared under nodes: but named by no topics:, services: or actions: entrywire it or remove it
depth-mismatch, qos-mismatch, type-mismatchboth sides name it and disagreechange one side
period-mismatcha timer’s period differs from its path’s rate_hzchange one side
param-missing-in-contract, param-phantom, param-type-mismatchthe same, for a node’s params:as above

Only phantom and param-phantom rows are waivable, one row at a time, in the bringup’s system.toml. The message prints the key to copy:

[census.waive."<node>:sub:/chatter"]
reason = "..."

A refusal that begins census missing: or census stale: means the census is absent or older than the code. Run nros build <image> for the cross image, which takes the census first. If it reports that the bringup declares no HOST image resolved from the same launch and arguments, add one, such as [image.linux] board = "linux" with the same launch and args.

Example

The C++ workspace in examples/workspaces/cpp declares both its nodes in src/demo_bringup/launch/system.contract.yaml. Delete the listener from it:

version: 1

nodes:
  talker:
    paths:
      on_timer:
        trigger: { timer: { rate_hz: 1 } }
        output: [chatter]
    pub:
      chatter: { qos: { depth: 1 } }

topics:
  /chatter:
    type: std_msgs/msg/String
    pub: [talker/chatter]

The listener’s code still subscribes to /chatter, so building the freertos image (board mps2-an385-freertos) is refused by the census: one missing-in-contract row for listener sub /chatter, whose remedy is the listener: entry with chatter: { qos: { depth: 1 } } and the sub: [listener/chatter] wiring. Put them back and the census confirms every row.

If it doesn’t work

  • A configure stops with ENTITIES was retired. A CMakeLists.txt still passes ENTITIES to nano_ros_node_register. Delete the argument and state the component’s entities in the contract.
  • [census] on_missing = "warn" or on_stale = "warn" is set. The bringup has opted out of the refusal, and the build prints a census missing (WARNING line instead. Remove the key to make it a refusal again.

See also