---
title: "verbatra doctor"
description: "Comprueba si un proyecto está bien configurado y recibe todos los problemas en un informe, sin llamar a un proveedor ni leer una clave de API."
type: "reference"
---
# verbatra doctor

Comprueba si un proyecto está bien configurado y recibe todos los problemas en un informe, sin llamar a un proveedor ni leer una clave de API.

<AvailableFrom version="0.9.0" />

Comprueba si un proyecto está bien configurado y recibe todos los problemas en un solo informe. `doctor` no construye ningún proveedor, no escribe ningún archivo y solo lee el valor de una clave de API con `--live`, que la envía en una petición de la lista de idiomas del proveedor (LibreTranslate no recibe ninguna). Úsalo después de [`verbatra init`](https://verbatra.kreitz-webdev.de/es/docs/cli/init.md) o cuando otro comando falle.

## Sinopsis [#sinopsis]

```bash
verbatra doctor [flags]
```

## Ejemplos [#ejemplos]

```bash
# report every setup problem at once
verbatra doctor

# validate a project in another directory, with an explicit config
verbatra doctor --cwd apps/web --config verbatra.config.ts

# machine-readable report for a CI preflight step
verbatra doctor --json

# list hardcoded user-facing strings, with no key set
verbatra doctor --literals

# what the provider supports for each target locale
verbatra doctor --locales

# the same, against the provider's current language list
verbatra doctor --live

# a data-flow manifest to attach to a DPA or NDA review
verbatra doctor --data-flow --json
```

## Flags [#flags]

| Flag | Argumento | Por defecto | Efecto |
| --- | --- | --- | --- |
| `--literals` | ninguno | desactivado | en lugar de revisar la configuración, busca strings visibles para el usuario escritos a mano en las raíces de código del [bloque `extract`](https://verbatra.kreitz-webdev.de/es/docs/config-file.md#extract) (ver [Literales sin traducir](#literales-sin-traducir)) |
| `--locales` | ninguno | desactivado | tras las comprobaciones, lista por locale de destino el código enviado, el soporte del proveedor, el glosario y la formalidad (ver [Soporte de locales](#soporte-de-locales)) |
| `--live` | ninguno | desactivado | primero descarga la lista de idiomas actual del proveedor, sin gastar cuota de traducción; implica `--locales` |
| `--data-flow` | ninguno | desactivado | en lugar de revisar la configuración, describe qué se envía a qué host y qué se escribe en local (ver [Flujo de datos](#flujo-de-datos)) |

También acepta los [flags globales](https://verbatra.kreitz-webdev.de/es/docs/cli.md#flags-globales) `--cwd`, `--config` y `--json`. `--locales` y `--live` no se pueden combinar con `--literals` (`INVALID_OPTION`, salida `2`), y `--data-flow` con ninguno de los tres.

## Comportamiento [#comportamiento]

### Qué comprueba [#qué-comprueba]

<AvailableFrom version="0.12.0" />

Una ejecución de configuración informa de estas comprobaciones, en este orden. Plural rules, Plural completeness, Locale codes, Locale state y Length limits son informativas y nunca fallan: cada una muestra `[warn]` cuando nombra algo y `[ok  ]` en caso contrario.

| Comprobación | Falla cuando |
| --- | --- |
| Configuration | no se encuentra ningún archivo de configuración o no pasa la validación |
| Format adapter | el `format` configurado no se resuelve a ningún adaptador de archivos |
| Provider | el `provider.id` configurado no se resuelve a ninguna factoría de proveedor |
| API key environment variable | la variable de la que ese proveedor lee su clave no está definida |
| Network policy | la [política de red](https://verbatra.kreitz-webdev.de/es/docs/network-policy.md) efectiva rechaza el host del proveedor o un proxy que usaría |
| Source locale file | el archivo de locale de origen falta en su ruta resuelta, no es un archivo normal o no se analiza con el formato configurado |
| Plural rules | nunca; nombra las versiones de ICU y CLDR en uso y avisa cuando un locale de destino no tiene reglas de plural en ICU |
| Plural completeness | nunca; avisa y nombra cada plural de destino al que le falta una categoría CLDR que usa su idioma, y juzga un plural sin formas en el destino por lo que una ejecución escribiría allí (una forma de origen vacía cuenta como ausente, y la generación de plurales solo donde de verdad se ejecuta), a propósito más estricto que el aviso de `translate`, solo para i18next (diez como máximo; [`verbatra check`](https://verbatra.kreitz-webdev.de/es/docs/cli/check.md#categorías-de-plural) lista los huecos de los plurales que un destino ya tiene); se omite para un formato que no guarda las formas de plural por categoría CLDR |
| Locale codes | nunca; avisa y nombra cada código válido pero no canónico, como `en-us` o `iw`, con su forma canónica (ver [Códigos de locale](https://verbatra.kreitz-webdev.de/es/docs/config-file.md#códigos-de-locale)) |
| Locale state | nunca; avisa y nombra cada locale con estado en el archivo de bloqueo, la caché o el archivo de procedencia que la configuración no lista, y si `translate` lo [traslada](https://verbatra.kreitz-webdev.de/es/docs/the-lock-file.md#un-código-de-locale-reescrito) |
| Length limits | nunca; avisa cuando `verbatra.limits.json` no se puede usar (una ejecución lo ignora y no se puede fijar un límite hasta arreglarlo), nombra un límite de una clave que la fuente ya no tiene o uno que el `maxLength` de la configuración sobrescribe, y lo dice si no se pudo leer la fuente para comprobar las claves; pasa cuando el archivo no existe ([límites de longitud](https://verbatra.kreitz-webdev.de/es/docs/config-file.md#maxlength)) <AvailableFrom version="0.13.0" /> |
| Locale support | el proveedor no admite el locale de origen o un locale de destino; avisa cuando un locale lleva un aviso (ver [Soporte de locales](#soporte-de-locales)) |

Cada comprobación se ejecuta aunque una anterior haya fallado. La única excepción es la configuración: si no se puede cargar, las comprobaciones que la necesitan aparecen como `[skip]` (`"status": "skipped"` en JSON), igual que Plural completeness para un formato sin formas de plural CLDR. Una comprobación que encuentra algo que merece atención sin fallar aparece como `[warn]` (`"status": "warn"` en JSON) y nunca cambia el código de salida. Una comprobación fallida lleva además un `fix`, un siguiente paso en imperativo que se imprime como línea `fix:` y viaja como `"fix"` en JSON, para que un agente actúe sin analizar `detail`.

Una ejecución con dos problemas:

```text output
verbatra doctor
  [ok  ] Configuration: Loaded /app/verbatra.config.ts.
  [ok  ] Format adapter: Format "i18next-json" resolves to an adapter.
  [ok  ] Provider: Provider "anthropic" resolves to a factory.
  [fail] API key environment variable: The ANTHROPIC_API_KEY environment variable is not set.
         fix: Set ANTHROPIC_API_KEY in the environment, or, with the CLI, in a .env file in the project directory.
  [ok  ] Network policy: any host (config: unset; VERBATRA_NETWORK_POLICY: unset). Provider "anthropic" connects to api.anthropic.com: permitted.
  [fail] Source locale file: The source locale file was not found at /app/locales/en.json.
         fix: Create the source locale file, or fix `files.pattern` and `sourceLocale` in the config so they point at it.
  ...
  [warn] Locale codes: "iw" is canonically "he". Consider the canonical form in your config; file names follow the configured code and are never renamed for you.
  [ok  ] Locale support: Provider "anthropic" is an LLM and accepts any locale (well-tested list of 2026-09-28).
2 problems found (run verbatra doctor again after fixing them)
```

### Claves y archivos [#claves-y-archivos]

- La clave de API se comprueba **solo por nombre**: `doctor` pregunta si la variable está definida, nunca qué contiene. La variable de cada proveedor está en [Proveedores](https://verbatra.kreitz-webdev.de/es/docs/providers.md).
- `openai-compatible` recurre a una clave de relleno, así que solo falla cuando la configuración nombra su propia variable en `provider.options.apiKeyEnvVar` y esa variable no está definida. `libretranslate` nunca hace fallar la comprobación, porque su clave es opcional.
- Con [`provider: { id: "none" }`](https://verbatra.kreitz-webdev.de/es/docs/human-only-workflow.md), las comprobaciones Provider, API key y Network policy pasan, y Locale support se omite.
- Que falte un archivo de **destino** no es problema: `translate` lo crea. El archivo de **origen** se analiza, así que un directorio, un archivo vacío o un contenido mal formado la hacen fallar.
- `doctor` carga primero los [archivos de entorno](https://verbatra.kreitz-webdev.de/es/docs/cli.md#archivos-de-entorno), así que una clave en un archivo dotenv cuenta como definida; con `--literals` no carga ninguno.

### Soporte de locales [#soporte-de-locales]

<AvailableFrom version="0.12.0" />

La comprobación Locale support pregunta si el proveedor puede traducir cada locale configurado. DeepL y Google Cloud Translation se evalúan contra una tabla de idiomas fechada que verbatra incluye; un proveedor LLM acepta cualquier locale. Falla justo en el locale que [`verbatra translate`](https://verbatra.kreitz-webdev.de/es/docs/cli/translate.md) rechazaría con `LOCALE_UNSUPPORTED_BY_PROVIDER` y, si no, muestra `[warn]` cuando un locale lleva un aviso o `--live` no pudo obtener la lista. [Soporte de idiomas](https://verbatra.kreitz-webdev.de/es/docs/language-support.md#comprobar-el-soporte-antes-de-una-ejecución) explica los veredictos.

`--locales` imprime el veredicto por locale después de las comprobaciones:

```text output
  locale support (deepl, language table of 2026-09-28, static)
    en  sent as EN  source, supported
    de  sent as DE  supported  glossary: yes  formality: yes
    sv  sent as SV  supported  glossary: yes  formality: no
      warning [FORMALITY_UNSUPPORTED_BY_PROVIDER] Provider "deepl" has no formality control for "sv", so the configured formal tone is not applied and the provider's default register is used.
    chr  sent as CHR  unsupported  glossary: no  formality: no
```

Cada línea nombra el locale, el código que recibe el proveedor (tras [`localeMap`](https://verbatra.kreitz-webdev.de/es/docs/providers.md#códigos-de-locale-enviados-al-proveedor), marcado con `(localeMap)`), el veredicto y si se aplican un glosario y un tono `formal` o `informal`. Un aviso es solo orientativo.

`--live` evalúa en cambio contra la lista actual del proveedor (DeepL `/v3/languages`, Cloud Translation `languages`, LibreTranslate `/languages`). Para LibreTranslate, que no incluye tabla, es la única forma de obtener un veredicto distinto de `unverified`, así que Locale support muestra `[warn]` hasta que se ejecute `--live`. La petición solo se envía si la variable de la clave está definida (LibreTranslate no la necesita) y la [política de red](https://verbatra.kreitz-webdev.de/es/docs/network-policy.md) permite el host; si no, o si falla, se usa la tabla incluida y una línea `live language list` explica por qué.

### Literales sin traducir [#literales-sin-traducir]

<AvailableFrom version="0.11.0" />

`--literals` encuentra strings visibles para el usuario que nunca llegaron a un catálogo. Recorre las raíces de código de tu [bloque `extract`](https://verbatra.kreitz-webdev.de/es/docs/config-file.md#extract), lee tu código sin escribirlo nunca, no construye proveedor, no lee ninguna clave y no carga ningún `.env`. Solo se ejecutan dos comprobaciones: Configuration y Untranslated literals. Cada hallazgo nombra archivo, línea y columna, con los espacios colapsados, las referencias de caracteres de JSX decodificadas y 80 caracteres como máximo:

```text output
  [fail] Untranslated literals: Scanned 42 source files: 2 untranslated literals found (1 suppressed).
    src/components/Header.tsx:12:9  "Welcome back"
    src/pages/settings.tsx:40:22  "Save changes"
    suppressed (directive) src/pages/legal.tsx:8:5  "Acme Inc."
```

Se informa de: texto JSX en archivos `.tsx`, `.jsx` y `.js`, atributos JSX visibles para el usuario (`alt`, `title`, `placeholder`, `label`, `aria-label` y los demás `aria-*` con texto) y cualquier otro string de dos o más palabras.

Nunca se informa de:

- un string pasado a `t(...)`, `$t(...)`, `i18n.t(...)` o a un `t` renombrado de `useTranslation`, o renderizado dentro de `<Trans>` o `<Translation>`;
- claves de objeto, especificadores de import, nombres de clase, valores CSS, ids de test, `data-*` y atributos de id o palabra clave (`aria-describedby`, `aria-labelledby`, `aria-controls`, `rel`, `sandbox`, `allow`, `autoComplete`, `referrerPolicy`, `crossOrigin`), URLs y strings sin letras;
- literales en posición de tipo (el valor antes de `as` o `satisfies` sí se comprueba), mensajes de log, mensajes de error (`new ValidationError(...)`, `throw Error(...)`), operandos de comparación y template literals con una expresión;
- argumentos directos de `describe` (zod), `query`, `execute`, `prepare`, `format`, `parse`, `z.enum`, `setItem`, `getItem` y `removeItem`, y el primer argumento de `on`, `off`, `once`, `emit`, `addEventListener` y de un `get` o `set` sobre un objeto; el texto más anidado y los mensajes pasados a funciones como `setError` sí se informan;
- todo lo que esté en archivos de test, story, declaración o configuración.

Para retener un literal, pon `// verbatra-ignore-next-line` encima (en JSX, `{/* verbatra-ignore-next-line */}`) o `// verbatra-ignore-line` al final de su línea. Una directiva de siguiente línea apunta a la siguiente línea con código y, en una etiqueta JSX de apertura, a todos sus atributos. Un string que vale en todas partes lo añades a `extract.literals.ignore` con las referencias decodificadas (`Tom & Jerry`). Los literales suprimidos se siguen listando con su motivo.

La comprobación falla con un hallazgo y con un archivo que no pudo abrir, leer hasta el final o que consideró demasiado grande (el escaneo continúa). Un proyecto sin bloque `extract` la hace fallar.

### Flujo de datos [#flujo-de-datos]

<AvailableFrom version="0.12.0" />

`--data-flow` describe adónde van los datos de este proyecto en lugar de revisar la configuración, como evidencia para una revisión de DPA o NDA; [Tratamiento de datos](https://verbatra.kreitz-webdev.de/es/docs/data-handling.md) explica cada categoría. No lee ninguna clave de API ni hace ninguna petición de red. Solo se ejecutan dos comprobaciones: Configuration y Data flow. Data flow nunca falla: muestra `[warn]` cuando la [política de red](https://verbatra.kreitz-webdev.de/es/docs/network-policy.md) rechaza un host o tiene un valor no válido, o cuando no se puede leer el archivo de origen para los recuentos.

```text output
  [ok  ] Data flow: Provider "deepl" sends source-text, placeholder-markers, language-codes to api.deepl.com (permitted), api-free.deepl.com (permitted).
  data flow (manifest version 1)
    provider     deepl (machine-translation)
    network      any host (no policy set)
    destination  api.deepl.com  default  paid-key  before-construction  permitted
    destination  api-free.deepl.com  default  free-key  before-construction  permitted
    sent         source-text, placeholder-markers, language-codes
    api key      required
    placeholders masked
    sensitive    off
    counts       3 source keys, 0 with a description or meaning, 1 with placeholders it cannot mask (withheld)
    locale       de  sent as DE  glossary terms: 0
    local        lock  verbatra.lock.json  (no source text, translations or personal data)
    local        provenance  verbatra.provenance.json  (reviewer names)
    local        cache  verbatra.cache.json  (source text, translations, gitignored by init)
    local        local-state  .verbatra-local  (source text, host name and process ID of a run holding a write lock, gitignored by init)
    request      language-list  verbatra doctor --live  sends the API key
    request      dns-lookup  each connection to a destination host
    agent        mcp  verbatra mcp  redactable
    agent        studio-agent-tools  verbatra studio --expose-agent-tools
```

El manifiesto nombra el proveedor; cada host de destino, de dónde sale (integrado, `provider.options.baseUrl` o una variable `*_BASE_URL`) y el veredicto de la política (`permitted`, `deferred`, `refused` o `invalid-policy`); los tipos de datos enviados, con el número de claves de origen, de claves con descripción o significado y de claves cuyos placeholders el proveedor no puede enmascarar (una estimación sobre las claves de origen: una ejecución solo envía claves que faltan o cambiaron y puede retener más con `sensitiveData: redact`); el código y el número de términos del glosario por locale de destino; los archivos locales con sus rutas relativas al proyecto y si contienen texto de origen, traducciones o datos personales; y las demás peticiones y superficies de agentes. DeepL aparece con sus dos hosts, porque la clave que elige uno nunca se lee. Un proxy en uso aparece con su variable y su host, nunca con sus credenciales, y los campos solo nombran lo que esta config puede enviar. Con `provider: { id: "none" }` indica que no se envía nada. Nombra hosts, categorías de datos y condiciones, nunca el valor de una clave ni el contenido de un archivo.

Con `--json` el manifiesto está en `result.dataFlow`. Su campo `version` solo cambia cuando se renombra o elimina un campo; pueden aparecer campos nuevos sin cambiarla, así que ignora los que no conozcas. [`dataFlowManifestSchema`](https://verbatra.kreitz-webdev.de/es/docs/sdk/inspect.md#dataflowmanifestschema) del SDK lo valida.

### Salida JSON [#salida-json]

Esquema: [`doctor-envelope.json`](https://verbatra.kreitz-webdev.de/schema/v1/doctor-envelope.json) valida el envoltorio completo; [Esquemas](https://verbatra.kreitz-webdev.de/es/docs/cli/output.md#esquemas) enumera los demás.

Con `--json`, el [envoltorio](https://verbatra.kreitz-webdev.de/es/docs/cli/output.md#salida-json) lleva el informe en `result`. `ok` es verdadero exactamente cuando el comando termina con `0`:

```ts
interface DoctorResult {
  ok: boolean;                 // true exactly when the command exits 0
  checks: DoctorCheck[];       // one per check, always in the same order
  literals?: LiteralScan;      // only with --literals: findings, suppressed, diagnostics
  dataFlow?: DataFlowManifest; // only with --data-flow: what goes where, version 1
  locales?: LocaleCapabilityReport; // per-locale provider support, with or without --locales
}

interface DoctorCheck {
  id:
    | "config"
    | "format-adapter"
    | "provider"
    | "api-key"
    | "network-policy"
    | "source-file"
    | "plural-rules"
    | "plural-completeness"
    | "locale-codes"
    | "locale-state"
    | "limits" // 0.13.0
    | "locales"
    | "untranslated-literals"
    | "data-flow";
  title: string;               // stable, human-readable name
  status: "pass" | "warn" | "fail" | "skipped";  // only "fail" makes ok false
  detail: string;              // why it reached that verdict; never an API key value
  fix?: string;                // the next step, only on a failed check
}

interface LocaleCapabilityReport {
  provider: string;
  coverage: "listed" | "open";       // "open" for an LLM provider
  tableVersion: string;              // the table's date (YYYY-MM-DD)
  tableOrigin: "static" | "live";    // "live" only when --live fetched the list
  live?: { status: "refreshed" | "skipped" | "failed"; detail: string }; // only with --live
  source: {
    locale: string;
    providerCode: string;
    mapped: boolean;
    support: "supported" | "unverified" | "unsupported";
    warnings: { code: string; message: string }[];
  };
  locales: {
    locale: string;
    providerCode: string;            // the code the provider receives
    mapped: boolean;                 // true when localeMap set providerCode
    support: "supported" | "unverified" | "unsupported";
    glossary: boolean;
    formality: boolean;
    warnings: { code: string; message: string }[];
  }[];
}
```

`result.locales` no aparece cuando no se pudo cargar la configuración, con un proveedor desconocido ni con `provider: { id: "none" }`.

### Información en el terminal [#información-en-el-terminal]

<AvailableFrom version="0.12.0" />

Mientras se ejecuta, `doctor` muestra una línea en stderr, que `--quiet` omite: `checking the setup`, `checking the setup and fetching the provider's language list` con `--live`, `describing the data flow` con `--data-flow` o `scanning the source for literals` con un recuento de archivos con `--literals`. El informe se queda en stdout. Consulta [Salida en el terminal](https://verbatra.kreitz-webdev.de/es/docs/cli/output.md#salida-en-el-terminal).

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

| Código | Significado |
| --- | --- |
| `0` | ninguna comprobación falló (un `warn` también sale con `0`) |
| `1` | al menos una comprobación falló, incluido un locale no admitido; con `--literals`, se encontró un literal o no se pudo escanear un archivo |
| `2` | no pudo ejecutarse: un error de uso o una ruta `--config` explícita que no existe |

Una configuración que la búsqueda no encuentra, o que no pasa la validación, es una comprobación fallida (salida `1`), porque informar de ello es justo la tarea del comando. Consulta [el contrato de códigos de salida](https://verbatra.kreitz-webdev.de/es/docs/cli/output.md#códigos-de-salida).

## Relacionado [#relacionado]

- [`verbatra init`](https://verbatra.kreitz-webdev.de/es/docs/cli/init.md) crea la configuración y el `.env.example` que valida `doctor`.
- [`verbatra check`](https://verbatra.kreitz-webdev.de/es/docs/cli/check.md) es la puerta contra la deriva cuando la configuración ya está bien.
- [El archivo de configuración](https://verbatra.kreitz-webdev.de/es/docs/config-file.md) documenta cada clave que valida `doctor`.
- [Proveedores](https://verbatra.kreitz-webdev.de/es/docs/providers.md) lista la variable de clave de API que lee cada proveedor.
- [Soporte de idiomas](https://verbatra.kreitz-webdev.de/es/docs/language-support.md#comprobar-el-soporte-antes-de-una-ejecución) lista las tablas de idiomas que usa la comprobación Locale support.
