StringLane

    Type to search titles, sections and body text across all 58 pages.

    How-To

    CLI Troubleshooting

    Command not found, no files detected, exit code 2, a stale or empty plan, a busy project, an MCP server that will not start, and a CI runner with no writable home.

    Last updated

    stringlane: command not found#

    The package installs a binary onto your PATH, so this means either the install did not happen or the shell has not picked it up.

    npm ls -g --depth 0

    If @stringlane/cli is not listed, install it: npm install -g @stringlane/cli. If it is listed, open a new shell, because a running one does not re-scan PATH.

    To use it without installing, run npx @stringlane/cli@latest check . and keep the @latest: named without a version, npx runs any copy already installed in the project or globally, however old.

    Node 22 or newer is required#

    node --version

    Upgrade Node, or use a version manager to select 22 or later for this project. There is nothing else to satisfy: the CLI ships as one file with no dependencies.

    No localization files were found#

    StringLane says this rather than reporting an empty project as a healthy one. First check that you are on CLI 0.4.0 or later (stringlane --version). From 0.4.0 these layouts are found with no stringlane.yaml:

    • easy_localization: JSON files such as en.json or en-US.json in assets/translations/ or assets/langs/.
    • slang: en.i18n.json in lib/i18n/ or assets/i18n/, and slang's namespaced widgets_en.i18n.json files. Locale-named folders (i18n/en/widgets.i18n.json) and a custom input_file_pattern without .i18n are not detected yet: set path for those.
    • A single Android module in any folder, such as mobile/src/main/res or core/ui/src/main/res, not only app/. Its keys carry no module prefix, and the path is saved to stringlane.yaml.

    From 0.7.0, i18next with one folder per language (locales/<language>/translation.json) is also found when you run the command from src/i18n or from the locales folder itself, not only from the repository root, and with a stringlane.yaml that names path: without layout:.

    Otherwise, two causes.

    Detection guessed wrong. Run stringlane init . and read what it proposes without accepting it. If the format or path is not right, write the correct one:

    format: android-xml
    path: app/src/main/res

    The layout is not a standard one. i18next in particular has two shapes; if each locale is a directory of namespace files rather than one file, say so:

    format: i18next-json
    path: locales
    layout: namespaced

    A path: that names a standard i18next folder, such as public/locales, loads that folder alone, and its keys carry no folder prefix. With no path:, every standard folder in the project loads together. If a first run recorded path: for you and you later add a second locales folder, remove path: to load both. A path: without layout: loads what layout: namespaced loads; layout: flat is still respected.

    From CLI 0.6.0, a stringlane.yaml that sets only fields such as defaultNamespace or baseLocale, with no path, no longer hides a namespaced i18next project. On an older CLI, keep path in the file.

    A file that will not load is named. A Localizable.stringsdict damaged by a git conflict or a cut-off write is a load error that names the file: resolve the conflict in it, or restore it from version control. In a namespaced i18next project, a translation file that is a symbolic link is not followed; it is reported as a load error that says it is a link.

    Exit code 2#

    The command could not run, which is deliberately distinct from exit 1. Run it without --json and read the message: it names the cause. Common ones:

    • a stringlane.yaml that does not parse, a path pointing at a directory that does not exist, or an unrecognised flag;
    • sourceStamps: true with .stringlane/source-stamps.json missing, unreadable, malformed or not a plain file. In CI this is usually a stamps file that was never committed, or a .gitignore line that hides .stringlane/. Restore it from version control, or run stringlane accept --all to create it;
    • a baseline that holds translation_stale or translation_unconfirmed. Run stringlane baseline write again: it leaves both out;
    • --fail-on translation_stale or translation_unconfirmed in a project with source stamps off, where the code can never fire;
    • stringlane accept in a project without sourceStamps: true. It prints the line to add and never edits stringlane.yaml itself;
    • a writing command with no terminal to ask at and no --yes;
    • stringlane update --parallelism with a value that is not a whole number from 1 to 8.

    --fail-on cannot turn a 2 into a 0, by design.

    STALE_PLAN#

    A file the plan fingerprinted changed between the plan being made and apply running. A plan fingerprints every locale file the project loads, not only the locales it targets, plus the .stringsdict beside each .strings file, stringlane.yaml and .stringlane/metadata.yaml, so a teammate's edit to app_de.arb stops an apply of a plan made with --target uk. The message names what moved.

    The same code, exit 2, also comes from validate and apply when the plan names a file the project does not manage any more: a locale file deleted, renamed or broken since the plan was made, or a plan edited by hand. Both refuse before they open anything. Since CLI 0.4.0 some of those cases exit 2 here where they used to exit 1 with a per-item problem, so if your CI branches on the exit code, this is the line to check.

    This is not an error to work around. Re-run stringlane plan and hand the agent the new one. The alternative is overwriting an edit that StringLane never read.

    Note that a context change is enough. Adding a description or changing productContext invalidates affected items even though the source English did not move, because a translation made without that context is not the same translation.

    PROJECT_BUSY#

    Another StringLane process holds the per-project lock: another apply, or, from desktop 1.3.0, the desktop app saving an edit to the same project. Wait for it.

    If you are certain nothing is running, because a previous run was killed, say, --force-unlock takes the lock and reports that it did. StringLane already reclaims a lock left by a dead process on its own, and reports that as a warning rather than doing it silently, so --force-unlock is only for the case where the process id has since been reused by something unrelated and the lock therefore looks alive forever.

    plan says there is nothing to translate#

    An empty plan says why, because zero items is good news only when everything is translated. The text report names the reason and the next step, and --json carries it as emptyReason:

    • incomplete: a locale file failed to parse, so the plan is not an answer about your project. stringlane check names the file.
    • no-targets: there is no locale to translate into. Add one with stringlane add-locale --locale <code>.
    • empty-source: the base has no value for the outstanding keys. Fill them in, or remove them.
    • unroutable: a locale has no file for one of the project's origins, so its keys have nowhere to go. The warning in the report names the origin.
    • complete: every target locale is translated. With source stamps on, it means nothing is left to translate; stringlane check can still report translations for a person to confirm with stringlane accept.

    A cell that is empty in your files is always offered as missing, even after you undo an apply with git checkout. Your files decide what is done. stale-state belongs to older CLIs, where a reverted cell could leave the plan empty; 0.6.0 and later never report it.

    apply refuses without a terminal#

    refusing to write your localization files without confirmation.
    stdin is not a terminal, so there is nobody to ask: pass --yes to confirm up front.

    Working as intended. If this is your own script and you mean it, pass --yes. If this is CI, reconsider: a pipeline that writes translations commits work nobody read. Gate on check instead.

    The MCP server does not appear in my agent#

    Check the CLI is reachable from the host. A host launched from a GUI does not inherit the PATH your terminal has. Either install globally so it lands somewhere the system path already covers, or put the absolute path in the config:

    which stringlane

    Check you restarted the host. MCP servers are read at startup.

    Check the entry is where the host reads it, and under the key it reads. stringlane setup mcp --host <host> prints the exact file and the entry in that host's own shape. This is the failure it exists to prevent: the eight known hosts want the entry under four different container keys, and one under the wrong key is not an error on any of them. The host starts, lists no StringLane tools, and says nothing about why. See Agent hosts.

    Confirm it is really connected. Ask the agent which locales the project has. If it answers by reading files itself rather than by calling inspect_project, the server is not attached.

    setup mcp refuses to write#

    It refuses rather than guessing, in three cases, and each is recoverable:

    • the file is not valid JSON: it will not replace a config it could not read, because that file holds every other MCP server you have set up. Fix the JSON;
    • the file is a symbolic link into a dotfiles repository: replacing it would turn your link into a plain file. Edit the real file, or add the entry by hand;
    • StringLane is already configured there with a different entry: it shows you both and waits for --force. An entry written by an older version names one project directory; --force replaces it with the one that covers them all.

    CI: no writable home directory#

    StringLane keeps a small amount of its own state outside your repository, in the usual per-user location for the platform. On a runner or in a container where that has nowhere to go, point it somewhere writable:

    export STRINGLANE_STATE_HOME="$RUNNER_TEMP/stringlane"

    It must be absolute. A relative value is ignored rather than honoured, because the state would otherwise land wherever the command happened to be started from.

    The Windows plugin resolver does not run#

    Expected. The plugin's bundled resolver is a POSIX shell script. Install the CLI yourself once and the MCP server and hooks behave identically:

    npm install -g @stringlane/cli

    Still stuck#

    Email support@stringlane.app with the command you ran and the output. stringlane check . --json is the most useful thing to attach: it names the format, the detected config and every finding, and it contains no source strings beyond the keys involved.

    Frequently asked questions

    Why does my agent not see the StringLane MCP tools?
    Usually because the process that starts the server cannot find stringlane on its PATH: a host launched from a GUI does not inherit the PATH your shell has. Install the CLI globally, or give the absolute path as the command in the MCP entry, then restart the host.
    Why does stringlane say no localization files were found?
    Either the format or the path is wrong, or the project layout is not one detection recognises. Run stringlane init to see what it detects, and set format and path in stringlane.yaml explicitly if the guess is wrong.

    Free ยท In your coding agent

    Run the same checks from your coding agent

    Paste this into Claude Code, Cursor, Codex or five other agents. It sets up the CLI and MCP server for this project, proposing each command and waiting for your yes.

    Read https://stringlane.app/docs/cli/agent-setup.md and set up StringLane for this project.

    Or install it yourself:

    npm install -g @stringlane/cli