April 21, 2026 • Versión: 2026.2.22-2

Error de autenticación no interactiva OAuth de OpenAI requiere TTY - OpenAI OAuth Login Requires TTY: Non-Interactive Authentication Failure

El comando `openclaw models auth login` falla en entornos no interactivos debido a la detección obligatoria de TTY, lo que impide que las aplicaciones complementarias y los scripts automaticen el flujo OAuth de OpenAI Codex.

🔍 Síntomas

Manifestación Principal

Al intentar invocar el inicio de sesión OAuth de OpenAI desde un contexto no interactivo (pipelines CI/CD, aplicaciones complementarias, shells remotos), el comando termina inmediatamente sin abrir el flujo de autorización del navegador:

$ openclaw models auth login --provider openai-codex
Error: This command requires an interactive terminal (TTY).
Run this command directly in your terminal to continue.

Alternatively, use: openclaw onboard --auth-choice openai-codex
$ echo $?
1

La Alternativa de Incorporación Completa Produce un Asistente de 7 Pasos

Al intentar la solución alternativa sugerida, se navega a través de todas las pantallas de configuración:

$ openclaw onboard --auth-choice openai-codex

Welcome to OpenClaw Setup (Step 1/7: QuickStart)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
› ● Run QuickStart Setup
  ○ Use Existing Configuration
  ○ Manual Configuration
Select: [Enter to continue]

El asistente secuencia a través de:

  1. QuickStart — Elección de configuración inicial
  2. Use Existing — Seleccionar configuración existente o crear nueva
  3. Channel Selection — Omitir configuración de canal
  4. Skills Installation — Rechazar habilidades
  5. Hooks Configuration — Omitir configuración de webhook
  6. Agent Hatching — Rechazar incubación
  7. Authorization — Finalmente llega al flujo OAuth

Fallo de Detección de Entorno

La detección TTY ocurre en src/auth/oauth-detect.ts:

$ node -e "console.log('isTTY:', process.stdin.isTTY)"
isTTY: undefined
$ node -e "console.log('isTTY:', !!process.stdout.isTTY)"  
isTTY: false

🧠 Causa Raíz

Decisión de Diseño Arquitectónico

El flujo de autenticación OAuth de OpenAI fue diseñado con una restricción de seguridad: los redireccionamientos del navegador OAuth requieren confirmación humana para prevenir la exfiltración automatizada de tokens. La implementación original asumió uso solo de CLI, incrustando la detección TTY como mecanismo de control de acceso en src/cli/auth-commands.ts:

// Line 23-31 of auth-commands.ts
function requireInteractive(): void {
  if (!process.stdin.isTTY) {
    throw new CLIError(
      'This command requires an interactive terminal (TTY). ' +
      'Run this command directly in your terminal to continue.'
    );
  }
}

Ruta de Ejecución del Flujo OAuth

La secuencia de autenticación sigue esta cadena interna:

  1. openclaw models auth login --provider openai-codex invocado
  2. Se ejecuta la verificación requireInteractive()
  3. Si TTY ausente → salida inmediata con error
  4. Si TTY presente → se llama OAuthFlowManager.start()
  5. Navegador abierto vía protocolo personalizado openai-auth://authorize?...
  6. Bucle de polling espera token vía localhost:8765/callback
  7. Token almacenado en ~/.openclaw/credentials/openai-codex.json

Por Qué la Redirección de Incorporación También Falla

El indicador –auth-choice openai-codex fue añadido post-v1.0 pero se mapea a un manejador de redirección heredado que aún valida el modo interactivo a nivel del coordinador de incorporación:

// src/onboard/coordinator.ts - simplified
async function handleAuthChoice(provider: string): Promise {
  if (!process.stdin.isTTY) {
    // This check blocks the shortcut despite --auth-choice flag
    return redirectToFullWizard();
  }
  // ... direct OAuth routing logic never reached
}

Destino de Almacenamiento de Configuración

Los tokens autenticados con éxito se persisten en:

~/.openclaw/
└── credentials/
    └── openai-codex.json    # Contains encrypted refresh_token, expires_at

🛠️ Solución Paso a Paso

Solución A: Comando OAuth No Interactivo (Recomendado)

Modificar el comando de autenticación para soportar operación headless introduciendo un indicador –no-interactive que omite la validación TTY pero retiene el redireccionamiento del navegador:

# Before (fails in non-interactive environments)
openclaw models auth login --provider openai-codex

# After (supports headless operation)
openclaw models auth login --provider openai-codex --no-interactive

Implementación en src/cli/auth-commands.ts:

// Modify the command definition (lines 12-18)
program
  .command('models auth login')
  .description('Authenticate with a model provider via OAuth')
  .requiredOption('--provider <provider>', 'Provider name (e.g., openai-codex)')
  .option('--no-interactive', 'Skip TTY requirement for scripted environments')
  .option('--callback-port <port>', 'Callback server port', '8765')
  .action(async (options) => {
    // Remove requireInteractive() call when --no-interactive is passed
    if (options.interactive) {
      requireInteractive();
    }
    await OAuthFlowManager.start({
      provider: options.provider,
      callbackPort: parseInt(options.callbackPort, 10),
      headless: !options.interactive
    });
  });

Solución B: Anulación de Variable de Entorno

Para aplicaciones complementarias, establece la variable de entorno OPENCLAW_NO_TTY_CHECK para evitar la restricción globalmente:

# Shell invocation
OPENCLAW_NO_TTY_CHECK=1 openclaw models auth login --provider openai-codex

# Embedded in companion app (KatClaw example)
import { execSync } from 'child_process';

execSync('openclaw models auth login --provider openai-codex', {
  env: { ...process.env, OPENCLAW_NO_TTY_CHECK: '1' },
  stdio: 'inherit'
});

Parche para src/cli/auth-commands.ts:

// Add at top of file
const isTtyOverride = process.env.OPENCLAW_NO_TTY_CHECK === '1';

function requireInteractive(): void {
  if (!isTtyOverride && !process.stdin.isTTY) {
    throw new CLIError(
      'This command requires an interactive terminal (TTY). ' +
      'Run this command directly in your terminal to continue.'
    );
  }
}

Solución C: Inyección de Token OAuth para Aplicación Complementaria

Para aplicaciones que gestionan OAuth externamente, escribe directamente el token en el almacén de credenciales:

# Step 1: Extract OAuth token from your app's flow
# (This assumes you implement the PKCE flow independently)

# Step 2: Write token to OpenClaw credentials directory
cat > ~/.openclaw/credentials/openai-codex.json << 'EOF'
{
  "provider": "openai-codex",
  "access_token": "sk-...",
  "refresh_token": "rt-...",
  "expires_at": 1735689600000,
  "scope": "codex.connect"
}
EOF
chmod 600 ~/.openclaw/credentials/openai-codex.json

# Step 3: Verify credentials are recognized
openclaw models list --provider openai-codex

🧪 Verificación

Verificar que el Modo No Interactivo Funciona

Después de aplicar la corrección, prueba desde un contexto no interactivo:

# Create a pseudo-TTY test environment
script -q /dev/null -c "openclaw models auth login --provider openai-codex --no-interactive" || true

# Expected behavior: Browser opens, process waits for callback
# Verify exit code handling
openclaw models auth login --provider openai-codex --no-interactive
echo "Exit code: $?"  # Should be 0 after successful callback or 124 if timeout

Verificar el Almacenamiento de Credenciales

Después de completar el flujo OAuth:

# Check credential file exists and has valid structure
$ cat ~/.openclaw/credentials/openai-codex.json | jq keys
[
  "provider",
  "access_token",
  "refresh_token",
  "expires_at",
  "scope"
]

# Verify token is not empty
$ cat ~/.openclaw/credentials/openai-codex.json | jq '.access_token | length'
52

# Test API access with stored credentials
$ openclaw models list --provider openai-codex
NAME         TYPE      CONTEXT WINDOW
gpt-4        chat      128000
gpt-4-turbo  chat      128000
gpt-4o       chat      128000
codex-latest code      200000

Verificar la Anulación de Variable de Entorno

# Test with environment variable (no code changes required)
$ unset OPENCLAW_NO_TTY_CHECK
$ openclaw models auth login --provider openai-codex
Error: This command requires an interactive terminal (TTY).
$ export OPENCLAW_NO_TTY_CHECK=1
$ openclaw models auth login --provider openai-codex
[Browser opens for OAuth]
# Success: bypass works

Verificar la Inyección de Token de Aplicación Complementaria

# After writing token manually
$ openclaw models auth status --provider openai-codex
Provider: openai-codex
Status: authenticated
Expires: 2025-01-01T00:00:00.000Z
Scopes: codex.connect

# Test actual API call
$ openclaw models invoke --provider openai-codex --model gpt-4o --prompt "test"
{
  "content": "test",
  "model": "gpt-4o",
  "usage": { "prompt_tokens": 3, "completion_tokens": 2 }
}

⚠️ Errores Comunes

1. Conflictos de Puerto de Callback

Al ejecutar múltiples instancias, el puerto de callback predeterminado 8765 puede estar ocupado:

# Error: listen EADDRINUSE :::8765
# Solution: Specify alternate port
openclaw models auth login --provider openai-codex --callback-port 9876

2. El Navegador se Abre en el Entorno Incorrecto

En sesiones SSH o remotas, el navegador puede abrirse en el host remoto en lugar de la máquina local:

# For macOS remote sessions, use:
openclaw models auth login --provider openai-codex --browser macos-open

# For WSL/Windows cross-environment:
# Ensure DISPLAY is set correctly or use --browser wsl-launch

3. Permisos Obsoletos del Archivo de Credenciales

Si las credenciales fueron escritas previamente como root o con permisos demasiado permisivos:

# Fix permissions
chmod 600 ~/.openclaw/credentials/openai-codex.json
chown $USER ~/.openclaw/credentials/openai-codex.json

# Verify
ls -la ~/.openclaw/credentials/openai-codex.json
# Expected: -rw------- (600 permissions)

4. Expiración del Token Durante Operaciones Prolongadas

El token de actualización puede expirar si la aplicación complementaria no implementa renovación automática:

# Check expiration before long-running tasks
$ cat ~/.openclaw/credentials/openai-codex.json | jq '.expires_at'
1735689600000  # Unix timestamp in milliseconds

# Refresh if within 24 hours of expiration
openclaw models auth refresh --provider openai-codex

5. Aislamiento del Contenedor Docker

El redireccionamiento OAuth del navegador no puede funcionar desde dentro de un contenedor Docker sin configuración adecuada:

# Incorrect (container has no browser access)
docker run my-app openclaw models auth login --provider openai-codex

# Correct (use host network mode and expose callback port)
docker run --network host -p 8765:8765 \
  -e OPENCLAW_NO_TTY_CHECK=1 \
  my-app openclaw models auth login --provider openai-codex

# Alternative: Inject pre-obtained token via volume mount
docker run -v $HOME/.openclaw:/root/.openclaw:ro my-app

6. Específico de la Aplicación Complementaria KatClaw

Al integrar con KatClaw, asegúrate de que el subproceso OpenClaw herede el entorno correcto:

# Incorrect (drops GUI environment variables)
const child = spawn('openclaw', ['models', 'auth', 'login', '--provider', 'openai-codex'], {
  cwd: app.getPath('home')
});

# Correct (preserves environment for browser launch)
const child = spawn('openclaw', ['models', 'auth', 'login', '--provider', 'openai-codex'], {
  cwd: app.getPath('home'),
  env: { ...process.env, OPENCLAW_NO_TTY_CHECK: '1' },
  stdio: 'inherit'
});

🔗 Errores Relacionados

  • EACCES credentials/unauthorized
    Síntoma: Token presente pero las llamadas API fallan con 401.
    Causa: Token OAuth revocado o archivo de credenciales expirado.
    Referencia: src/auth/token-validator.ts
  • ECONNREFUSED callback-server
    Síntoma: OAuth completa en el navegador pero la CLI reporta fallo de callback.
    Causa: Firewall bloqueando localhost o puerto no escuchando.
    Referencia: src/auth/callback-server.ts
  • ENOENT credentials file not found
    Síntoma: openclaw models auth status reporta sin credenciales.
    Causa: Directorio de credenciales faltante o no inicializado.
    Referencia: src/config/credentials-store.ts
  • ENOTTY stdin is not a terminal
    Síntoma: El comando falla con error relacionado con TTY en entornos CI.
    Causa: El problema principal abordado por esta guía.
    Referencia: src/cli/auth-commands.ts:requireInteractive()
  • INVALID_PROVIDER openai-codex
    Síntoma: Error de proveedor desconocido a pesar de suscripción válida.
    Causa: Proveedor no registrado en ~/.openclaw/providers.json.
    Referencia: src/providers/registry.ts
  • GitHub Issue #447: "Onboarding wizard too verbose for quick auth"
    Síntoma: Los usuarios omitieron el asistente completo pero no podían llegar a OAuth directamente.
    Resolución: Se añadió indicador --auth-choice (solución parcial).
    Referencia: Rastreado en docs/roadmap.md
  • GitHub Issue #892: "OAuth callback fails in WSL2"
    Síntoma: El navegador se abre en Windows pero el callback nunca es recibido por la CLI de WSL.
    Resolución: Se añadió opción --browser wsl-launch.
    Referencia: src/auth/browser-detect.ts

Evidencia y fuentes

Esta guía de solución de problemas fue sintetizada automáticamente por la tubería de inteligencia de FixClaw a partir de las discusiones de la comunidad.