El CLI tecsas3-cli usa exit codes estables para que los orquestadores (systemd, GitHub Actions, Airflow, cron, scripts bash) puedan enrutar errores sin parsear stderr. Cada subclase de TecsaError define su propio exit_code y el handler central en cli/src/tecsas3_cli/cli.py:run() los traduce a sys.exit(...). El rango 0–5 se reserva para clases de error conocidas y predecibles; el 99 absorbe cualquier excepción no esperada.

Tabla de exit codes

La asignación anterior es la implementada en cli/src/tecsas3_cli/errors.py y en el handler run() de cli/src/tecsas3_cli/cli.py. Los códigos 0 y 1 son los únicos compartidos por varias clases: NotFoundError y ValidationError colapsan a 1 para simplificar el branching en shell (if ! tecsas3 ...; then ...).

Mapeo desde HTTP status

El factory from_http_status(status, message, ...) centraliza la conversión. Cualquier 4xx/5xx que llegue del backend se traduce a la subclase apropiada y conserva http_status, hint y request_id para diagnóstico.

Ejemplos por exit code

Patrones de scripting

Branching simple por éxito / fallo

Reintentos sólo en ServerError (exit 2)

Enrutamiento por tipo de error en CI

Inspección del error completo

Con --json, los errores se serializan con todos los campos de diagnóstico:
request_id aparece cuando el backend lo emite en X-Request-ID y permite correlación con los logs del servidor.

Troubleshooting

Exit code inesperado en pipes

Si quieres el comportamiento clásico (exit del último comando), quita pipefail o usa:

Exit 2 persistente tras reintentos

ServerError ya incluye 2 reintentos con backoff 0.5s/1s. Si el backend sigue 5xx tras 3 intentos totales, el CLI emite exit 2. Posibles causas:
  • Caída real del servicio — revisa el dashboard de salud del tenant.
  • Mantenimiento programado — espera la ventana y reintenta más tarde.
  • Bug reproducible — captura el request_id y repórtalo a soporte.

Exit 3 (AuthError) inmediato tras login

El login fue OK pero el siguiente comando falla con 401. Causa típica: el backend invalida tokens emitidos con un SECRET_KEY distinto al actual (rotación de claves o reinicio de entorno). Solución:

Exit 4 (ConfigError) con TOML válido

load_settings ignora silenciosamente secciones [profile.<x>] que no parsean. Verifica que el perfil que estás invocando existe:

Exit 5 (NetworkError) sólo en CI

El runner no tiene acceso al backend (firewall corporativo, NAT, DNS privado). Diagnóstico:

Exit 99 con stacktrace verboso

El handler run() imprime code=UNEXPECTED y la excepción cruda a stderr. Para reportarlo:

Siguientes pasos