Skip to main content

Linting

Workflows4s includes a static linter that detects common issues in workflow definitions before runtime. Run it on your workflow definition to catch problems early.

Usage

val workflow: WIO[?, ?, ?, ?] = ???
val issues: List[LinterIssue] = Linter.lint(workflow)
// Each issue contains: message, ruleId, and path

Each LinterIssue includes a path showing the location within your workflow tree (e.g., root > flatMap > branch[approval]).

Built-in Rules

clashing-signals

Detects when the same signal is expected in multiple parallel branches or in both a base workflow and its interruption trigger. This leads to ambiguity about which branch should handle the signal.

Fix: Ensure each signal type is only awaited in one branch at a time.

clashing-events

Detects when the same event type can be generated in parallel branches. This causes non-deterministic recovery since the event could match multiple handlers during replay.

Fix: Use distinct event types for parallel branches, or restructure to avoid parallel event generation.

busy-loop

Detects loops that contain no signals or timers. Such loops would spin indefinitely without waiting for external input.

Fix: Add a signal handler or timer inside the loop body.

unnecessary-error-handler

Detects error handlers applied to workflows that cannot produce errors. This is harmless but indicates unnecessary complexity.

Fix: Remove the error handler, or verify the wrapped workflow actually needs error handling.