Every visitor handler inDocumentation Index
Fetch the complete documentation index at: https://mintlify.com/mpsuesser/effect-oxlint/llms.txt
Use this file to discover all available pages before exploring further.
effect-oxlint — and the create generator itself — carries a fixed error channel of never. This is not an arbitrary constraint; it follows directly from how oxlint’s plugin API works and how effect-oxlint bridges it with Effect. Understanding this constraint helps you write rules that are both correct and resilient.
Why the error channel is never
oxlint’s plugin API is synchronous. When Rule.define converts an effectful visitor into a plain oxlint visitor, it does so using Effect.runSync at the FFI boundary. Effect.runSync can execute synchronous effects and return their results, but it has no mechanism to surface a typed Effect.fail — if the running effect fails, Effect.runSync throws an uncaught exception, which would crash the linter for the entire file being processed.
To make this contract explicit, the EffectHandler type enforces never as the error channel:
create generator in Rule.define:
create and each handler must have error type never at the type level, which TypeScript enforces at compile time.
What this means in practice
You cannot useEffect.fail directly in a handler or let an error propagate upward uncaught. Any sub-effect that can fail must have its failure handled inside the handler before the Effect.Effect<void, never, RuleContext> is returned.
Typed failures (
Effect.fail) must be caught. Defects (Effect.die, thrown exceptions) propagate out of Effect.runSync and are not caught at the handler boundary — reserve them for genuine invariant violations.Handling fallible sub-effects
The recommended pattern is to catch the failure inside the handler and surface it as a diagnostic usingctx.report. This turns a potential crash into a visible lint message, which is far more useful to the rule consumer.
Effect.catch receives the typed error value and must return an Effect<void, never, RuleContext>. Because ctx.report satisfies that type, the catch handler can be a direct call with a descriptive message.
Where Effect.runSync is called
Effect.runSync appears in exactly two places inside Rule.define (in src/Rule.ts):
Once per file — running create
When oxlint begins linting a new file, it calls the
create function on every registered rule. Rule.define runs the create generator via Effect.runSync, allocating all Ref state and building the visitor map. The RuleContext service is provided before runSync is called, so yield* RuleContext inside create always succeeds.Once per node — running each handler
After
create returns the visitor map, Rule.define wraps every handler so that when oxlint visits a node, the returned Effect<void, never, RuleContext> is executed immediately via Effect.runSync. RuleContext is provided here too, so handlers have full access to ctx.report, ctx.filename, and ctx.sourceCode.Effect.runSync yourself. It is internal infrastructure in Rule.define — an implementation detail of the FFI bridge.
Defects vs. typed failures
Effect distinguishes two kinds of failures:| Kind | How produced | Caught by Effect.catch? |
|---|---|---|
| Typed failure | Effect.fail(error) | Yes |
| Defect | Effect.die(cause) or thrown exception | No |
never error channel constraint prevents typed failures from escaping handlers. Defects, on the other hand, are not caught at the handler boundary and will propagate out of Effect.runSync, crashing the linter for the current file. Reserve defects for situations that genuinely represent a bug — not for expected runtime conditions like a missing AST node or an absent import.
Note on test files
Rule test files are allowed to usethrow and try/catch because effect-oxlint’s oxlint configuration explicitly disables the avoid-untagged-errors and avoid-try-catch lint rules for test files. This is a deliberate exception to the domain-code rule that all errors should be typed Effect failures.
Effect patterns
The full set of Effect idioms used in effect-oxlint rules.
Assembling a plugin
How to collect your rules into an oxlint plugin with
Plugin.define.