Exit Codes and the JSON Report
What each of the four stringlane exit codes means, the shape of the --json envelope, and the machine-readable refusal codes apply can return.
Last updated
Exit codes#
The separation of 1 and 2 is load-bearing. A missing stringlane.yaml, an unreadable file or a bad flag is not the same event as a genuinely broken translation, and a pipeline that treats them alike tells you the wrong thing on a bad morning.
3 narrows 2 further, and today only one thing produces it: the npm registry gave no answer to stringlane upgrade. The command itself was fine and your project is not implicated, so calling it 2 would say you invoked it wrong. Nothing about your translations can produce a 3, and neither can the background update check — that one leaves the exit code untouched whatever the registry does, so a check job never sees one.
--fail-on can only narrow which findings produce 1. It cannot suppress a 2 or a 3.
Per-command variations:
planexits0even when there is a great deal to translate, because a plan is a list rather than a gate. It exits1only when a file failed to parse, since a plan built from an unreadable file is incomplete rather than empty.validateexits1when the candidates have problems.applyexits0when everything was applied,1when the candidates have problems,2when it could not run — which includes every refusal below.upgradeexits0whether you are current or behind, since being behind is not an error, and3when the registry gave no answer.
The JSON envelope#
stringlane check . --json{
"protocolVersion": "1",
"command": "check",
"status": "issues",
"result": {
"kind": "single-origin",
"locales": ["de", "en", "fr"],
"baseLocale": "en",
"totalKeys": 8,
"completionPercent": 96,
"localeCoverage": [
{ "locale": "de", "totalCells": 9, "filledCells": 9, "missingCells": 0 },
{ "locale": "fr", "totalCells": 10, "filledCells": 9, "missingCells": 1 }
],
"localizationFiles": [
"res/values-de/strings.xml",
"res/values-fr/strings.xml",
"res/values/strings.xml"
],
"parseErrors": [],
"loadIssues": [],
"configWasAbsent": false,
"configState": "present",
"format": "android-xml",
"detectedConfig": { "format": "android-xml", "path": "res" },
"issues": [
{
"key": "item_count",
"locale": "fr",
"severity": "error",
"code": "missing_key",
"message": "Plural quantity \"many\" for \"item_count\" is missing in \"fr\"",
"quantity": "many"
}
],
"issueCounts": { "error": 1, "warning": 3, "info": 0 }
},
"warnings": []
}Envelope fields#
Coverage fields#
Counts are cells, not keys. A plural key contributes one cell per quantity the target language requires, which is why a locale requiring one, few, many and other can show a higher totalCells than one requiring two forms. completionPercent is derived from cells across the project.
Issue fields#
code is the field to branch on. message is written for a person and is allowed to be reworded.
Refusal codes#
apply and the writing MCP tools report a machine-readable reason when they decline. Nothing is written on any of them.
WRITE_FAILED is returned when the write itself could not happen — the project directory is not writable, the filesystem is read-only, or there is no space. Nothing is written on it either.
Two issue codes, unused_key and undefined_key, are produced only by the source-code scan, so check --no-scan --fail-on unused_key is refused rather than accepted: with the scan off nothing can emit that code, and a gate that can never fire looks exactly like a clean project.
A candidate naming a key and locale the plan does not contain is refused rather than ignored. Quietly dropping an agent's typo produces a missing translation nobody ever notices.
Frequently asked questions
- What does stringlane exit code 2 mean?
- The command could not run: a missing or invalid config, an unreadable project, a bad flag. It is deliberately distinct from exit 1, which means the command ran and found localization issues, and from exit 3, which means the command was fine but something it depends on did not answer. A broken gate and a failing gate are different answers.
- Is the stringlane --json output stable?
- The envelope carries protocolVersion "1" and is the contract to build against. Pin the CLI version in CI anyway: at 0.x the command surface is not promised, and the version number is how that is said.
MCP Tool Reference
Next →Agent Hosts: What Yours Gets