---
title: "Códigos de salida y salida JSON"
description: "El contrato de códigos de salida, el envoltorio JSON de cada comando con --json, los códigos propios de la CLI y cómo escribe verbatra en stderr."
type: "reference"
---
# Códigos de salida y salida JSON

El contrato de códigos de salida, el envoltorio JSON de cada comando con --json, los códigos propios de la CLI y cómo escribe verbatra en stderr.

Todo aquello en lo que un script o un paso de CI puede confiar en todos los comandos. Cada [página de comando](https://verbatra.kreitz-webdev.de/es/docs/cli.md) añade solo lo específico de ese comando.

## Códigos de salida [#códigos-de-salida]

El código de salida es el contrato sobre el que tu paso de CI toma su decisión:

| Código | Significado |
| --- | --- |
| `0` | éxito, por comando como se indica bajo la tabla |
| `1` | la ejecución llegó al final, pero el resultado no está limpio: `translate` o `import` terminó con al menos un locale fallido o parcial, `check` encontró un locale desincronizado (o, con `--qa`, una traducción confirmada con un error de integridad o, con `--strict`, con una advertencia de revisión o un plural incompleto, o, con `--require-reviewed`, una traducción escrita por máquina que nadie aprobó, o, con `--sensitive`, contenido que parece sensible en el archivo de origen o el glosario), `check --file` encontró un error de sintaxis o de integridad en el archivo (o, con `--strict`, una advertencia de revisión o un plural incompleto), `diff` encontró una clave ausente o cambiada (o, con `--unused`, un análisis completo encontró una clave de origen sin uso), `report provenance` encontró `verbatra.provenance.json` corrupto o escrito por un verbatra más nuevo, `doctor` encontró al menos una comprobación fallida, `types --check` encontró que la declaración confirmada está desactualizada, o `studio` o `mcp` falló al apagar su servidor |
| `2` | no se pudo ejecutar: un error de toda la ejecución (configuración incorrecta, origen ilegible, archivo de bloqueo corrupto, un locale que el proveedor no admite), un error de uso (una lista `--locales` vacía, `INVALID_LOCALES`, o un `--debounce` o `--port` inválido), un valor `--locales` no configurado (`UNKNOWN_LOCALE`), `init` al que le falta un flag que necesita, que encuentra varios formatos o patrones de archivo que encajan, que se niega a sobrescribir una configuración existente o que es incapaz de generar una configuración válida, un arranque o una parada fallidos de `watch`, `studio` incapaz de cargar la configuración, de importar `@verbatra/studio` o de arrancar su servidor, o `mcp` con un `--config` que nombra una ruta inexistente, o incapaz de importar `@verbatra/mcp`, de arrancar su servidor o de aplicar `--redact-values` con el `@verbatra/mcp` instalado (una configuración ausente o inválida que encuentra la búsqueda lo arranca sin configurar) |
| `3` | `translate` (también una ejecución de prueba y `--estimate`) en un proyecto [solo humano](https://verbatra.kreitz-webdev.de/es/docs/human-only-workflow.md) (`provider: { id: "none" }`) terminó sin errores, pero dejó claves para una traducción humana; un locale fallido o parcial sigue saliendo con `1` |
| `130` | `translate` o `import` se interrumpió con Ctrl-C (SIGINT) tras liberar sus bloqueos de escritura, o `watch`, `studio` o `mcp` fue detenido a la fuerza por una segunda interrupción |
| `143` | `translate` o `import` se detuvo con SIGTERM tras liberar sus bloqueos de escritura |

El código `0` significa, por comando:

- `translate` o `import`: cada locale terminó completo
- `check`: todos los locales están sincronizados
- `diff`: no hay cambios pendientes
- `report`: imprimió su informe
- `doctor`: ningún problema de configuración del proyecto
- `extract`: completó su análisis
- `pseudo`: generó su pseudolocale
- `types`: generó su declaración
- `export`: escribió su libro
- `tmx`: importó o exportó su memoria de traducción
- `init`: preparó el proyecto o lo encontró ya al día
- `watch`, `studio` o `mcp`: se detuvo limpiamente
- `--help` o `--version`: imprimió la ayuda o la versión

Con `130` y `143`, stderr termina con `verbatra: interrupted (SIGINT), released locks` (o `SIGTERM`), o, con `--json`, con el registro `{"type":"interrupted","signal":"SIGINT","locksReleased":true}`. Si liberar los bloqueos falla o no termina en 5 segundos, la línea lo dice en su lugar (`could not release locks: <reason>` o `lock release did not finish within 5s`), y el registro JSON lleva `"locksReleased":false` con `"reason":"failed"` (más `message`) o `"reason":"timed-out"` (más `deadlineMs`).

Algunos casos límite que conviene conocer:

<AvailableFrom version="0.9.0" />

- Un locale **parcial** cuenta como fallo. Significa que el archivo se escribió pero todavía faltan algunas claves, normalmente porque un sublote del proveedor falló o la puerta de integridad rechazó una traducción. Sale con `1` exactamente igual que un locale fallido. Lee `partial` en el resumen para distinguir los dos casos. Un sublote fallido deja en el locale un aviso `SUB_BATCH_FAILED` que nombra el código de error del proveedor, o `PROVIDER_CALL_FAILED` cuando el fallo no fue un error estructurado del proveedor.
- Un **locale que el proveedor no admite** también es un error de toda la ejecución, nunca un único locale fallido. `translate` (incluidos un dry run y `--estimate`), `watch` al arrancar y la retraducción de una sola clave lo rechazan con `LOCALE_UNSUPPORTED_BY_PROVIDER` y salen con `2` antes de construir ningún proveedor o gastar nada, así que ningún locale de la ejecución ha empezado y no se ha escrito nada. Se evalúa contra la tabla de idiomas que verbatra incluye para DeepL y Google Cloud Translation; un proveedor LLM acepta cualquier locale. Deja fuera ese locale con `--locales` para traducir los demás, y ejecuta [`verbatra doctor --locales`](https://verbatra.kreitz-webdev.de/es/docs/cli/doctor.md#soporte-de-locales) para ver por qué. Consulta [Soporte de idiomas](https://verbatra.kreitz-webdev.de/es/docs/language-support.md#comprobar-el-soporte-antes-de-una-ejecución).
- Un **archivo de bloqueo corrupto** es un error de toda la ejecución tanto en `translate` como en `import`, nunca un único locale fallido. Es un solo archivo compartido, así que un archivo de bloqueo que se corrompe con la ejecución en marcha la detiene con `2` en vez de seguir con los locales restantes. Lo que se escribió antes del corte permanece en disco: repara el archivo de bloqueo y vuelve a ejecutar el comando.
- Una sola interrupción es una parada limpia, y los tres comandos de larga duración (`watch`, `studio` y `mcp`) salen con `0` ante ella. Más allá de eso no des por hecho que se comportan igual: si la propia parada falla, `watch` sale con `2` mientras que `studio` y `mcp` salen con `1`.
- Una ejecución fallida durante `watch` aparece como un registro en el flujo de salida, nunca como un código de salida distinto de cero.
- `export` no tiene modo de fallo por locale: sale con `0` o `2`, nunca con `1`.
- `doctor` lee una configuración rota de forma distinta al resto de comandos: una configuración que la búsqueda no encuentra, o una que no supera la validación, es una comprobación fallida y da `1`, porque informar precisamente de eso es el trabajo del comando. Sale con `2` solo cuando no puede ejecutarse en absoluto, por ejemplo con una ruta `--config` explícita que no existe.
- Hay un modo de fallo que queda fuera del contrato: un fallo de análisis que no es un error de uso se vuelve a lanzar, y el binario no lo captura, así que se aplica el comportamiento por defecto de Node ante un rechazo no gestionado en lugar de cualquiera de los seis códigos.
- Una clave [protegida](https://verbatra.kreitz-webdev.de/es/docs/protecting-human-translations.md) (un valor obsoleto que escribió, importó o cambió una persona fuera de verbatra, o una clave fijada) no cambia el código de salida de `translate` en un proyecto con proveedor: sale con `0` e indica en stderr cuántas claves dejó (las claves fijadas no cuentan). `check` sigue saliendo con `1` por ella, porque la clave sigue obsoleta hasta que una persona la resuelva. En un proyecto solo humano cuenta para la salida `3`.

## Salida JSON [#salida-json]

Todos los comandos salvo `studio` y `mcp` aceptan `--json` para una salida legible por máquina en stdout: `translate`, `watch`, `check`, `diff`, `report`, `export`, `import`, `tmx`, `doctor`, `pseudo`, `types`, `extract` e `init`. Cada registro ocupa una línea, y cada registro va dentro del mismo envoltorio, así que decides según un solo campo y nunca tienes que adivinar de qué comando son los datos que tienes delante:

```ts
type Envelope<TResult> =
  | { ok: true; version: 1; command: string; result: TResult }
  | {
      ok: false;
      version: 1;
      command: string | null;
      code: string;
      message: string;
      causeCode?: string; // the code of the error this one wraps, when it has one
      candidates?: string[]; // the values to choose from, on an error that has them
      missing?: string[]; // the flags still to pass, on an init usage error
      hint?: string; // the next step that resolves the error, when there is one
    };
```

`version` es la versión de esta forma de envoltorio, no la versión del paquete. Es un entero, así que lo comparas con `===` en lugar de analizar un rango, y solo cambia cuando un campo existente cambia de significado o desaparece. Pueden aparecer campos nuevos sin incrementarla, así que ignora los que no reconozcas.

Una ejecución que falla por completo escribe exactamente un registro `ok: false` en stdout y sale con `2`. Su `code` es el mismo código de error estable que lleva la línea de stderr, y es el valor sobre el que decides:

```json
{ "ok": false, "version": 1, "command": "translate", "code": "CONFIG_INVALID", "message": "..." }
```

`command` es `null` solo cuando el fallo ocurrió antes de resolver un subcomando. `candidates` aparece solo en un error que te pide elegir, como `FORMAT_AMBIGUOUS` de `init`, y lista los valores entre los que elegir. `missing` aparece en un error de uso de `init` que deja flags sin responder y nombra cada flag que aún tienes que pasar. `causeCode` aparece cuando el error envuelve otro error con código, como un `PROVIDER_CONSTRUCTION_FAILED` causado por `MISSING_API_KEY`; la línea de stderr termina entonces con `(cause: MISSING_API_KEY)`.

<AvailableFrom version="0.12.0" />

`hint` aparece cuando el error tiene un siguiente paso: una frase breve en imperativo que lo resuelve, como `"Set GEMINI_API_KEY in the environment, or, with the CLI, in a .env file in the project directory."` para una clave ausente o `` "Run `verbatra check --help` to see the options and arguments it accepts." `` para un `USAGE_ERROR`. Actúa según él en lugar de analizar `message`. Nombra variables, flags y rutas, nunca el valor de una clave. Cada código del SDK, de un proveedor y de un adaptador de formato tiene uno (el SDK exporta el mismo texto como [`errorHint`](https://verbatra.kreitz-webdev.de/es/docs/sdk/errors.md#errorhint)), y también cada [código de error de la CLI](https://verbatra.kreitz-webdev.de/es/docs/error-codes.md#cli-error-codes) excepto `CLI_ERROR`. Sin `--json`, el mismo texto sigue a la línea de error en stderr como `next: ...`, salvo que uses `--quiet`. `mcp --json` es la excepción: se rechaza con una sola línea `USAGE_ERROR` en stderr, sin envoltorio y sin `next:`. Los errores de arranque de `studio` y `mcp` tampoco llevan una línea `next:`.

El `result` de un envoltorio correcto es distinto en cada comando y se describe en su página, bajo Salida JSON: [`extract`](https://verbatra.kreitz-webdev.de/es/docs/cli/extract.md), [`translate`](https://verbatra.kreitz-webdev.de/es/docs/cli/translate.md), [`watch`](https://verbatra.kreitz-webdev.de/es/docs/cli/watch.md), [`check`](https://verbatra.kreitz-webdev.de/es/docs/cli/check.md), [`diff`](https://verbatra.kreitz-webdev.de/es/docs/cli/diff.md), [`report`](https://verbatra.kreitz-webdev.de/es/docs/cli/report.md), [`doctor`](https://verbatra.kreitz-webdev.de/es/docs/cli/doctor.md), [`pseudo`](https://verbatra.kreitz-webdev.de/es/docs/cli/pseudo.md), [`types`](https://verbatra.kreitz-webdev.de/es/docs/cli/types.md), [`export`](https://verbatra.kreitz-webdev.de/es/docs/cli/export.md), [`import`](https://verbatra.kreitz-webdev.de/es/docs/cli/import.md), [`tmx`](https://verbatra.kreitz-webdev.de/es/docs/cli/tmx.md), [`init`](https://verbatra.kreitz-webdev.de/es/docs/cli/init.md).

## Esquemas [#esquemas]

<AvailableFrom version="0.12.0" />

Cada registro que imprime `--json` tiene un JSON Schema (draft 2020-12). Los esquemas vienen dentro de los paquetes de npm y se sirven en URL estables, para que un script, un agente o un editor pueda validar la salida en lugar de leer prosa. Cada esquema de salida admite propiedades que no enumera, como promete el contrato del envoltorio de arriba; mantenlo así si copias uno. El esquema de la configuración es la excepción a propósito: describe lo que tú escribes, así que rechaza una clave desconocida para detectar una errata.

| Documento | URL | Archivo en el paquete |
| --- | --- | --- |
| Cualquier envoltorio, de éxito o de error | [`envelope.json`](https://verbatra.kreitz-webdev.de/schema/v1/envelope.json) | `@verbatra/cli/schemas/envelope.json` |
| El envoltorio de un comando, con su `result` | `https://verbatra.kreitz-webdev.de/schema/v1/<command>-envelope.json` | `@verbatra/cli/schemas/<command>-envelope.json` |
| El envoltorio de error | [`error-envelope.json`](https://verbatra.kreitz-webdev.de/schema/v1/error-envelope.json) | `@verbatra/cli/schemas/error-envelope.json` |
| El resultado de `init` por sí solo, sin envoltorio | [`init-result.json`](https://verbatra.kreitz-webdev.de/schema/v1/init-result.json) | `@verbatra/cli/schemas/init-result.json` |
| Una línea JSON en stderr: progreso, espera de un bloqueo, interrupción | [`stderr-record.json`](https://verbatra.kreitz-webdev.de/schema/v1/stderr-record.json) | `@verbatra/cli/schemas/stderr-record.json` |
| El archivo de configuración | [`config.json`](https://verbatra.kreitz-webdev.de/schema/v1/config.json) | `@verbatra/sdk/schemas/config.json` o `@verbatra/sdk/config-schema.json`: el mismo esquema sin `$id` ni `title` |

Cada página de comando enlaza su envoltorio en Salida JSON, y [https://verbatra.kreitz-webdev.de/schema/v1](https://verbatra.kreitz-webdev.de/schema/v1) enumera cada documento con su título. El envoltorio de un comando apunta al esquema de su `result` por URL en `$ref`, así que un validador lo carga por la red, o desde `node_modules/@verbatra/sdk/dist/schemas` si registras antes esos archivos.

`v1` sigue la `version: 1` del envoltorio y siempre sirve la última versión publicada. Un campo nuevo se queda en `v1`; un cambio que rompe una carga útil sube la `version` del envoltorio y añade `/schema/v2/`. El esquema de la configuración usa el mismo esquema de URL, pero sigue los cambios de la propia configuración. La URL siempre sirve la última versión publicada, mientras que una copia de npm se queda en su versión, así que una copia antigua puede rechazar un registro de una CLI más nueva allí donde un esquema enumera valores cerrados, como el `type` de un registro de stderr o el `command` de un envoltorio.

## Códigos de error de la CLI [#códigos-de-error-de-la-cli]

<AvailableFrom version="0.12.0" />

La CLI lanza estos códigos por sí misma. La misma lista se publica como `CLI_ERROR_CODES` (con el tipo `CliErrorCode`) desde `@verbatra/cli`, así que un script puede comprobar un código contra ella. Cualquier otro código viene del SDK, de un proveedor o de un adaptador de formato. Qué significa cada código, qué comandos lo lanzan y cuál es el siguiente paso está en [Códigos de error](https://verbatra.kreitz-webdev.de/es/docs/error-codes.md#cli-error-codes).

## Errores en stderr [#errores-en-stderr]

Todo error va a stderr como una línea estructurada (`verbatra: error [CODE] message`) en ambos modos. Desde 0.12.0 eso incluye un error al analizar los argumentos (una opción o un comando desconocidos, un argumento ausente): imprime `verbatra: error [USAGE_ERROR] unknown option '--nope'` y sale con `2`, donde antes imprimía el propio `error: unknown option '--nope'` de commander, sin código. `init` sigue la misma gramática desde 0.12.0; antes, un proveedor ausente imprimía `verbatra: --provider is required (...)` sin código. Desde 0.12.0, sin `--json` una pista sigue a la línea como `next: ...`, y con `--json` un error de análisis escribe en stdout el envoltorio `USAGE_ERROR` con su `hint`; `mcp` reserva stdout para el protocolo, así que rechaza `--json` solo en stderr. El arranque de `studio` y `mcp` es la excepción a la línea estructurada: si `@verbatra/studio` o `@verbatra/mcp` no está instalado, stderr lleva solo la instrucción de instalación, sin el prefijo `verbatra:`. Con `--json`, el `message` de un error de análisis es el texto propio de commander, con el prefijo `error: `. Sin `--json`, una ejecución fallida sigue sin escribir absolutamente nada en stdout. Los registros de progreso y de espera de bloqueo van siempre a stderr, así que stdout no lleva más que envoltorios.

## Salida en el terminal [#salida-en-el-terminal]

<AvailableFrom version="0.12.0" />

Los resultados van a stdout. El progreso, los avisos, las sugerencias, las advertencias y los errores van a stderr, y verbatra los adapta según a dónde vaya stderr:

- En un terminal interactivo, las etiquetas fijas como `error`, `[ok]` y `next:` llevan color, y un paso que tarda más de 300 ms muestra un spinner ASCII (`| / - \`).
- Cuando stderr se redirige o va a una tubería, `CI` tiene un valor distinto de vacío, `0` o `false`, o `TERM` vale `dumb`, la salida son líneas simples que solo se añaden, sin color ni animación.
- Las etiquetas de estado del informe de [`doctor`](https://verbatra.kreitz-webdev.de/es/docs/cli/doctor.md) en stdout (`[ok  ]`, `[warn]`, `[fail]`, `[skip]`) tienen color cuando stdout es un terminal interactivo; un informe redirigido queda sin formato salvo que `FORCE_COLOR` active el color.
- `--json` nunca añade color ni animación, y stdout no lleva más que envoltorios. Las líneas simples de aviso `verbatra:` aún pueden llegar a stderr, así que quien consume la salida analiza solo las líneas de stderr que empiezan por `{`. Consulta [Salida JSON](https://verbatra.kreitz-webdev.de/es/docs/cli/output.md#salida-json).

| Flag o variable | Efecto |
| --- | --- |
| `-q`, `--quiet` | muestra solo resultados, advertencias y errores: omite las líneas de progreso, las sugerencias `next:` y las líneas informativas de stderr, mientras que el resumen del resultado, avisos incluidos, se mantiene. Funciona antes o después del nombre del comando |
| `--no-color` | nunca colorea la salida |
| `NO_COLOR`, `NODE_DISABLE_COLORS`, `VERBATRA_NO_COLOR` | cualquier valor no vacío desactiva el color; `VERBATRA_NO_COLOR` también se impone a `FORCE_COLOR` |
| `FORCE_COLOR` | `1`, `2`, `3`, `true` o un valor vacío activa el color aunque stderr o stdout no sea un terminal, y cualquier otro valor, `0` incluido, lo desactiva. Si está definido, se impone a `NO_COLOR`, `NODE_DISABLE_COLORS`, `CI` y `TERM=dumb`; `--no-color` y `VERBATRA_NO_COLOR` siguen teniendo prioridad |
| `VERBATRA_NO_SPINNER` | cualquier valor no vacío sustituye el spinner por líneas estáticas `label... done (1.2s)`, para lectores de pantalla y grabaciones del terminal |

Los ejemplos `output` de las páginas de comando muestran la forma simple. Un terminal interactivo muestra un paso en curso como una línea con spinner que termina en `[ok]` y el tiempo transcurrido.

Las palabras de estado son ASCII simple (`[ok]`, `[warn]`, `[fail]`, `[skip]`), así que la salida se lee igual en cualquier terminal y registro. Cada línea pasa por la misma ocultación de claves que el resto de la salida, y solo se colorean las etiquetas fijas, nunca una clave, una ruta ni un mensaje.
