JSON diagnostics protocol
ftl-lint --format json is the stable interface for editor and tool integration. Human-readable output may evolve independently; integrations should consume this protocol instead.
Invocation and streams
Pass files or standard input exactly as in human mode:
ftl-lint --format json locales/pl/messages.ftl
ftl-lint --format json - < locales/pl/messages.ftl
A valid invocation writes one JSON document to standard output. Invocation and I/O errors go to standard error. The JSON document is compact; examples in this guide are formatted for readability.
Schema version 1
{
"schema_version" : 1 ,
"diagnostics" : [
{
"path" : "locales/pl/messages.ftl" ,
"severity" : "error" ,
"code" : "E0003" ,
"message" : "expected token `}`" ,
"labels" : [
{
"primary" : true ,
"message" : "unexpected `?` inside the placeable" ,
"span" : {
"start" : { "byte" : 30 , "line" : 0 , "column" : 26 },
"end" : { "byte" : 31 , "line" : 0 , "column" : 27 }
}
}
],
"notes" : [],
"help" : ["close the placeable before `?`" ]
}
]
}
The top-level object contains:
| Field | Type | Meaning |
|---|---|---|
|
integer |
Protocol version; currently |
|
array |
Zero or more diagnostic objects |
Each diagnostic contains:
| Field | Type | Meaning |
|---|---|---|
|
string |
Input path, or |
|
string |
|
|
string |
Stable Fluent or project lint code |
|
string |
Diagnostic summary |
|
array |
Labeled source spans |
|
array of strings |
Supporting context |
|
array of strings |
Suggested actions |
Every label contains:
| Field | Type | Meaning |
|---|---|---|
|
boolean |
Whether this is the diagnostic’s primary label |
|
string |
Explanation attached to the span |
|
object |
Half-open source range with |
The diagnostics, labels, notes, and help arrays are always present, including when they are empty.
Source positions
Each position contains three zero-based values:
| Field | Meaning |
|---|---|
|
UTF-8 byte offset from the beginning of the input |
|
Line number |
|
Unicode scalar-value offset from the beginning of the line |
Spans are half-open: start is inclusive and end is exclusive. Empty spans have identical start and end positions. LF, CRLF, and lone CR each advance the line once and reset the column to zero.
Columns count Unicode scalar values, not bytes, UTF-16 code units, grapheme clusters, or display cells. Consumers that index UTF-8 source can use byte; editors should translate line and column into their native position type.
Exit and error behavior
The JSON format preserves the command’s normal exit statuses:
| Result | Standard output | Standard error | Status |
|---|---|---|---|
All inputs clean |
Empty diagnostics document |
Empty |
|
Diagnostics found |
Diagnostics document |
Empty |
|
An input cannot be read |
Diagnostics from successfully read inputs |
I/O error |
|
Invalid invocation |
No JSON document |
Error and usage |
|
An I/O failure may therefore produce an empty or partial diagnostics document. Consumers must check the process status and standard error as well as parse standard output.