Validation & Linting

39 diagnostic checks — 9 structural errors and 30 semantic warnings — to catch problems before runtime.

Overview

Dippin provides two levels of analysis:

Structural validation (DIP001-DIP009): Errors that must be fixed. A workflow with any of these cannot execute. Run with dippin validate.

Semantic linting (DIP101-DIP133): Warnings that flag likely bugs or questionable patterns. They don't block execution but should be reviewed. Run with dippin lint for both levels.

Diagnostic Format

Diagnostics are displayed in a rustc-inspired format:

error[DIP003]: unknown node reference "InterpretX" in edge
  --> pipeline.dip:45:5
  = help: did you mean "Interpret"?

Structural Errors (DIP001-DIP009)

These must be fixed for a workflow to be valid. Each causes exit code 1.

DIP001 — Start Node Missing

The workflow must declare a start: field pointing to an existing node.

error[DIP001]: start node does not exist
  --> pipeline.dip:1:1
  = help: add "start: <NodeID>" to the workflow header
DIP002 — Exit Node Missing

The workflow must declare an exit: field pointing to an existing node.

error[DIP002]: exit node does not exist
  --> pipeline.dip:1:1
  = help: add "exit: <NodeID>" to the workflow header
DIP003 — Unknown Node Reference in Edge

Every edge's From and To must reference existing node IDs. The validator uses Levenshtein distance to suggest corrections for typos.

error[DIP003]: unknown node reference "InterpretX" in edge
  --> pipeline.dip:45:5
  = help: did you mean "Interpret"?
DIP004 — Unreachable Node from Start

Every node must be reachable from the start node via some path of edges. BFS from the start node cannot reach this node.

error[DIP004]: node unreachable from start
  --> pipeline.dip:20:3
  = help: add an edge leading to this node, or remove it
DIP005 — Unconditional Cycle Detected

The workflow graph must be a DAG, with the exception of restart edges. A back-edge not marked restart: true would loop forever.

error[DIP005]: unconditional cycle detected
  --> pipeline.dip:50:5
  = help: remove an edge in this cycle or mark it "restart: true"
DIP006 — Exit Node Has Outgoing Edges

The exit node is the terminal — it must have zero outgoing edges.

error[DIP006]: exit node has outgoing edges
  --> pipeline.dip:55:5
  = help: remove outgoing edges from the exit node
DIP007 — Parallel/Fan-In Mismatch

Every parallel node must have a matching fan_in node with the same set of branch nodes.

error[DIP007]: parallel fan-out/fan-in mismatch
  --> pipeline.dip:15:3
  = help: add a matching fan_in node
DIP008 — Duplicate Node ID

Node IDs must be globally unique within a workflow.

error[DIP008]: duplicate node ID
  --> pipeline.dip:30:3
  = help: rename this node or remove the duplicate
DIP009 — Duplicate Edge

No two edges may have the same (from, to, condition) combination. Edges with different conditions on the same pair are not duplicates.

error[DIP009]: duplicate edge
  --> pipeline.dip:60:5
  = help: remove the duplicate edge

Semantic Warnings (DIP101-DIP133)

These flag likely bugs or questionable patterns. Warnings alone exit 0.

DIP101 — Node Only Reachable via Conditional Edges

All incoming edges have conditions — if none match, execution can never reach this node. Automatically suppressed when source nodes have exhaustive conditions (e.g., success/fail pairs).

warning[DIP101]: node "NextPhase" is only reachable through conditional edges
  --> pipeline.dip:25:3
  = help: add an unconditional edge, or verify conditions are exhaustive
DIP105 — No Success Path to Exit

There is no guaranteed path from start to exit through unconditional edges alone. If conditions don't match, execution may never reach the exit.

warning[DIP105]: no success path from start to exit
  --> pipeline.dip:1:1
DIP108 — Unknown Model/Provider

The model or provider isn't in the recognized list. Verified against official provider documentation.

warning[DIP108]: unknown model/provider combination
  --> pipeline.dip:15:5
DIP110 — Empty Prompt on Agent

An agent node has no prompt text. An agent without a prompt won't produce meaningful output.

warning[DIP110]: empty prompt on agent node
  --> pipeline.dip:12:3
DIP111 — Tool Without Timeout

A tool node has no timeout field. Without a timeout, a hanging command blocks the pipeline indefinitely.

warning[DIP111]: tool command has no timeout
  --> pipeline.dip:35:3
DIP115 — Goal Gate Without Recovery Path

A node has goal_gate: true but no retry_target or fallback_target, meaning the pipeline has no recovery path if the gate fails.

warning[DIP115]: node "validate_tests" has goal_gate but no recovery path
  --> pipeline.dip:18:3
  = help: add retry_target or fallback_target
DIP123 — Tool Command Syntax Error

The tool command block has a shell syntax error detectable by bash -n — unclosed quotes, bad redirects, missing fi/done.

warning[DIP123]: tool command has shell syntax error:
  unexpected EOF while looking for matching `"'
  --> pipeline.dip:45:5
DIP124 — Runtime Variable in Tool Command

A tool command contains ${ctx.*} interpolation. These are Dippin runtime variables that expand to empty strings in the shell.

warning[DIP124]: tool command references ${ctx.api_url}
  which expands to empty at runtime
  --> pipeline.dip:50:5
DIP125 — Binary Not Found

The first command in the tool block references a binary not on the current PATH. This is a hint — the deployment environment may differ.

hint[DIP125]: tool command binary "npx" not found on PATH
  --> pipeline.dip:55:5
DIP126 — Subgraph ref file does not exist

A subgraph node's ref: path does not resolve to an existing file on disk.

DIP127 — Invalid human node mode

The mode: value on a human node must be choice, freeform, or interview.

warning[DIP127]: invalid human node mode "dialog"
  --> pipeline.dip:22:5
  = help: use one of: choice, freeform, interview
DIP128 — Interview mode with meaningless default

A human node with mode: interview has a default: value, which has no effect in interview mode — answers come from the extracted question list.

warning[DIP128]: human node "GatherRequirements" has mode: interview with a default value
  --> pipeline.dip:28:5
  = help: remove the default field — it is ignored in interview mode
DIP129 — Interview mode with choice-style labeled edges

A human node with mode: interview has outgoing edges with choice labels, which conflict — interview mode collects freeform answers, not discrete choices.

warning[DIP129]: human node "GatherRequirements" uses interview mode but has choice-style edges
  --> pipeline.dip:30:5
  = help: remove edge labels or switch to mode: choice
DIP130 — Invalid response_format value

The response_format: field on an agent node must be json_object or json_schema. Any other value is unrecognised and will be rejected at runtime.

warning[DIP130]: invalid response_format "xml" on agent "Parse"
  --> pipeline.dip:18:5
  = help: use "json_object" or "json_schema"
DIP131 — response_schema / response_format mismatch

Two related hints: (1) if response_schema: is set but response_format: is not json_schema, the schema will be ignored; (2) if response_format: json_schema is set but no response_schema: is provided, the model receives no schema to enforce.

warning[DIP131]: response_schema set but response_format is not json_schema
  --> pipeline.dip:20:5
  = help: set response_format: json_schema or remove response_schema
DIP132 — response_schema is not valid JSON

The value of response_schema: must be a valid JSON object. Malformed JSON will cause a runtime error when the model attempts to apply structured output.

warning[DIP132]: response_schema on agent "Extract" is not valid JSON
  --> pipeline.dip:22:5
  = help: fix the JSON syntax in response_schema
DIP133 — params key shadows a first-class field

A key inside the agent's params: block (e.g. model or provider) duplicates a first-class field on the same node. The first-class field takes precedence; the params entry is silently ignored.

hint[DIP133]: params key "model" shadows the first-class model field on agent "Analyze"
  --> pipeline.dip:35:7
  = help: remove the params key and set the field directly