Skip to main content
Two commands answer most of it.
Lint messages name the rule they broke and the page that explains it. Start there.

Lint stops you

The node runs but nothing happens

An output stays empty. Nothing emits to it. Open api/events.yaml and check there is a row whose emit matches the connector name. A streaming node emits nothing. Its rows need match, naming the event type they fire on. Without one, a row on a streaming transport fires on everything or nothing, and lint will tell you which. A downstream node receives one word at a time. The row needs accumulate: true, so it emits the running total rather than each fragment. The node never runs at all. A required input has nothing wired to it. The node is waiting, which is a legitimate state, so nothing reports an error.

A template resolved to empty

This is the one that wastes the most time, because nothing errors. A path that matches nothing resolves to empty and says nothing about it. The usual causes:
  • The node id is wrong. signal.quote1.quote needs the id Canvas gave that node. Check it against the edge you actually drew.
  • The output name is wrong. It has to match interface.yaml exactly.
  • You used input.*. There is no such root.
  • You used brackets. records[0].Name does not resolve. Use records.0.Name.
Run the node and look at what it printed for the sample data. If the value is not there, the path is wrong.

The service rejects the request

A number arrived as a string. A field that is exactly one {{ path }} keeps its type. Anything else, including a template with text around it, becomes a string. A 200 that is really a failure. Some services answer 200 with an error in the body. Add error to the call, and it will surface instead of flowing downstream as nonsense.
401 from the service. Check the credential value in Canvas, then check scheme against the service’s own documentation. Bearer and API-key-in-header are easy to confuse. Refused before it left. refusing a request to "x" means the host is not in the package’s allowedHosts. Add it, and remember it is checked again after templating, so a host built from config has to resolve to something on the list.

An expression failed

Expressions are sandboxed. They reshape data and nothing else. No process, require, fetch, eval, Function, new, assignment or constructor. Nothing mutates either, so .pop() fails where .at(-1) works. A rejected expression is logged and never runs.

The Agent ignores your node

It never saw it. The AI workflow builder asks for the nodes that suit a step and gets back a handful, so a node with weak whenToUse is invisible rather than rejected. Read Discoverability. The usual fault is an opening sentence about mechanism rather than the job.

The Agent ignores your tool

Same failure, one level down. A tool’s description is what the Agent reads to decide whether to call it. Write what the tool achieves. If the Agent has no tools at all, nothing is wired to the mcp connector.

Testing without the platform

unoverse node test needs no server. It reads keys from your own .env as <CREDENTIAL>_<FIELD> in upper snake case, so openAICredential.apiKey is OPENAI_API_KEY, and it names any that are missing before it runs. If a node passes on the bench and fails in a workflow, the difference is what reaches it. The bench uses testData.inputs; the workflow uses whatever the edges carry.

Still stuck