Metadata-Version: 2.4
Name: n8n-workflow-linter
Version: 1.1.0
Summary: Static production-readiness linter for exported n8n workflows
License-Expression: MIT
Project-URL: Repository, https://github.com/Payloadhq/n8n-workflow-linter.git
Keywords: n8n,linter
Requires-Python: >=3.10
Description-Content-Type: text/markdown
License-File: LICENSE
Dynamic: license-file

# n8n-workflow-linter by Payload

**Small software that earns its keep.** A free utility by Payload.

A zero-dependency Python CLI that statically lints an exported n8n workflow
JSON for production-readiness smells - the small misconfigurations that turn
into 3 a.m. pages.

## Install

Nothing. Python 3.10+ and this repo.

```bash
python3 lint.py workflow.json
```

## Usage

```bash
# lint a workflow exported from n8n (Download in the workflow menu)
python3 lint.py my-workflow.json

# machine-readable output
python3 lint.py my-workflow.json --json

# only check for N8N002 and N8N003
python3 lint.py --only N8N002 --only N8N003 my-workflow.json

# do not check for N8N002
python3 lint.py --ignore N8N002 my-workflow.json

# write safe automatic fixes to my-workflow.fixed.json
# the original export is NEVER overwritten
python3 lint.py --fix my-workflow.json
```

Exit codes: `0` = clean or selected issues fixed, `1` = errors found or a fixed-output conflict, `2` = warnings remaining.

`--fix` currently removes populated `pinData` only when N8N009 is selected. It writes
a separate `<workflow>.fixed.json` next to the source; it never modifies the
original or overwrites an existing fixed output. Review the generated file
before importing it into n8n.

Real output:

```
$ python3 lint.py my-workflow.json
WARN  [N8N002] node 'Call webhook': HTTP Request has no timeout configured
         fix: set options.timeout (ms), e.g. 10000, so a hung endpoint cannot stall the workflow
ERROR [N8N004] node 'Authed call': hardcoded secret in parameters.headerParameters.parameters[0].value (Bear...)
         fix: move the secret into n8n Credentials and reference it; never commit literal secrets in workflow JSON
1 error(s), 1 warning(s)
```

## Rules

| Code | Severity | What it flags |
|------|----------|---------------|
| N8N001 | warn | Non-trigger node without explicit `onError` handling |
| N8N002 | warn | HTTP Request node with no `options.timeout` |
| N8N003 | warn | HTTP Request node with `retryOnFail` disabled |
| N8N004 | error | Hardcoded credential/secret literal in node parameters (n8n `={{ }}` expressions are not flagged) |
| N8N005 | error | Workflow has no trigger node |
| N8N006 | warn | Workflow has multiple trigger nodes |
| N8N007 | warn | Disabled node left in the workflow |
| N8N008 | warn | Node with no connections at all |
| N8N009 | warn | Node with pinned data |

Notes on scope, stated plainly:

- Secret detection is heuristic: it looks for secret-like parameter names
  (`password`, `api_key`, `secret`, `token`, ...) and credential-style
  `{"name","value"}` pairs (e.g. an `Authorization` header) holding literal
  values. It is not a substitute for a secrets scanner.
- "Trigger" means a node whose type ends in `Trigger`, plus the `Start` and
  `Webhook` node types. Sticky notes are ignored.

## Tests

```bash
python3 tests/run_tests.py
```

10 fixtures: one clean workflow plus one workflow per smell. All must pass.

## What this doesn't do

This is a static linter, not a reliability system. It doesn't execute your
workflow, load-test it, simulate failure modes, add retries and circuit
breakers, or give you production-ready workflow templates.

For the full 10-workflow hardening system - failure alerting, timeout circuit
breakers, retry policies, and production patterns for AI agent workflows -
see the **[n8n Production AI Agent Reliability Kit](https://payloadtools.gumroad.com/l/n8n-agent-reliability-kit)**
($99) by Payload.

## License

MIT - see [LICENSE](LICENSE). Copyright 2026 Payload.

---

**Payload** - small, sharp tools for developers.
Developer portal: https://payloadhq.github.io/ ·
All products: https://payloadtools.gumroad.com/ ·
Contact: kylers.partners@gmail.com

---

**More from Payload** · [payloadhq.github.io](https://payloadhq.github.io/) · [all Payload repos](https://github.com/Payloadhq)

Related: [n8n-reliability-free-samples](https://github.com/Payloadhq/n8n-reliability-free-samples) · [n8n-nodes-payload-flow](https://github.com/Payloadhq/n8n-nodes-payload-flow)

## GitHub Action

Run the linter in CI on every exported workflow in your repo. Pin to a release rather than a moving branch:

```yaml
- uses: Payloadhq/n8n-workflow-linter@v1.1.0
  with:
    paths: 'workflows/**/*.json'
```

Inputs: `paths` (space-separated glob patterns, default `workflows/**/*.json`) and `fail-on-error` (default `true`).

The action installs the linter from this repository and checks every matched file. Workflows with lint errors fail the job when `fail-on-error` is true, so a workflow with a hardcoded secret or no trigger cannot merge unnoticed. Warnings are printed in the job log but never fail the job on their own. If no files match, the action passes and says so.
