TOML Linter
More than a parser. This checks the things a valid TOML file can still get wrong: inconsistent key casing, needless quoting, sections that jump back to a group defined earlier, arrays with mixed types, credentials sitting in plain text, and the usual whitespace debris.
What this tool does
It runs two sets of checks. Text-level rules read the raw file and can point at a line: trailing whitespace, missing final newline, CRLF endings, tab indentation, over-long lines, needlessly quoted keys, key casing, runs of blank lines, and keys whose names look like credentials but hold a literal value. Tree-level rules run after parsing: arrays with mixed element types, arrays of tables whose entries have different key sets, empty tables, and nesting deeper than your limit.
The intent it closes: "it parses, but is it any good?" A TOML validator answers a narrower question. Most config review comments — casing, grouping, a token in plain text — are style and hygiene, and those are what this reports.
Findings are graded. Errors are things that break parsing or clearly mean a mistake. Warnings are likely problems: a credential in the file, sections that interleave, trailing whitespace. Notes are house-style preferences you may not share, which is why the severity filter exists.
When you'd reach for it
- Before committing a config change. Catch the whitespace and casing comments before a reviewer writes them.
- Reviewing someone else's file. The findings give you concrete line numbers instead of a vague sense that the file is messy.
- After a conversion. Generated TOML often has odd quoting or ragged array-of-table entries; this points at them.
- Hunting for stray secrets. The credential rule flags keys whose value is a literal rather than an environment reference.
- Standardising a repo. Pick a key case and a line limit, then lint every config against them.
How it works
Text checks first — because they survive a parse failure — then structural ones.
1. Scan the raw text
Each line is examined on its own for whitespace debris, length, indentation style, and key shape. Quoted keys are compared against TOML's bare-key rules, so a key quoted for no reason is reported. Key names are tested against the case convention you picked. A value-bearing line whose key name matches your secret list is flagged unless the value is empty, a mask, or an environment placeholder such as ${VAR}.
2. Check the section layout
Table headers are collected in file order. If a section returns to a group that was already left behind — [a], then [b], then [a.c] — that is reported, because interleaved groups are the most common reason a config becomes hard to read. Optionally, top-level sections are also checked for alphabetical order.
3. Parse and inspect the tree
If the document parses, the value tree is walked for problems that are invisible line by line: an array mixing strings and numbers, entries of an array of tables that do not share the same keys, empty tables, and tables nested deeper than your limit. If it does not parse, the parser's error becomes an error-level finding with its line, and the text findings are still reported.
Options reference
Severity
Everything includes the style notes. Warnings and errors drops them, which is the setting for a pre-commit gate. Errors only reduces the output to things that are broken rather than merely untidy.
Key case
TOML allows both; pick the one your repo uses. snake_case is the most common in Python and Rust ecosystems, kebab-case in Cargo manifests and some JS tooling. Turn the check off for files with mixed conventions you cannot change — environment-variable keys, for instance, are conventionally SHOUTY.
Max line length / Max depth
Line length flags long inline tables and long arrays, which are usually better written as block tables or wrapped arrays. Depth flags over-nesting: past four or five levels, dotted keys or a flatter layout normally read better.
Section order / Secret keys
Alphabetical section order is off by default because deliberate grouping is often better than sorting. The secret list is the same substring matching the Redact tool uses — extend it with your own names (dsn, webhook, salt) to catch more.
Example
Input:
[Server]
Host = "0.0.0.0"
"port" = 8080
API_KEY = "sk-live-6f0c9a2b"
mixed = [1, "two", true]
Output:
0 error(s), 1 warning(s), 4 note(s)
line 2 INFO key-case Key 'Host' is not snake_case.
line 3 INFO needless-quotes Key "port" is quoted but bare form is legal.
line 4 INFO key-case Key 'API_KEY' is not snake_case.
line 4 WARN secret-in-config Key 'API_KEY' looks like a credential with a literal value.
line 1 INFO mixed-array Array at 'mixed' mixes value types.
The mixed-array finding comes from the parsed tree, so it is reported against line 1 rather than the array's own line — tree-level rules know the path, not the position. Everything with a real line number comes from the text pass.
Limits and notes
- Tree findings have no precise line. The parser does not retain source positions for values, so structural rules report the key path and line 1. The path is the useful part.
- It is not a schema validator. Nothing here knows that
portshould be between 1 and 65535. For that, generate a JSON Schema and validate against it. - Secret detection is name-based. A token under a bland key name is not flagged, and a key named
token_urlis flagged even though a URL is not secret. Treat it as a prompt, not a verdict. - Style rules are opinions. They are notes rather than warnings for exactly that reason — turn off the ones your project does not share.
FAQ
How is this different from the Validator?
The Validator answers one question: does this parse, and if not, where. The linter assumes it parses and looks for everything else — casing, quoting, grouping, mixed arrays, secrets, whitespace. Run the validator when you have an error; run the linter before you commit.
Can it fix the problems it finds?
Not directly — it only reports. The fixes live in other tools: the Formatter for quoting, spacing, and inline-table style, Sort for key order, and Redact for the credential findings.
Why is a key with an environment placeholder not flagged as a secret?
Because api_key = "${API_KEY}" is the pattern you want — the name matches but the value is a reference, not a credential. Empty strings and masks are skipped for the same reason.
What does the table-grouping warning mean?
That a section returned to a group that had already been left. [db], [cache], then [db.pool] is legal TOML but hard to read; keeping [db.pool] next to [db] is the fix.
Is the file uploaded?
No. Every rule runs in your browser, which matters given that one of them is specifically looking for credentials.