[resolveFallbackRetryPrompt descarta el prompt original del usuario al reintentar fallback del modelo] - resolveFallbackRetryPrompt Discards Original User Prompt on Model Fallback Retry
Cuando una llamada al modelo falla y activa el reintento de fallback, la función resolveFallbackRetryPrompt() reemplaza todo el prompt original del usuario con un mensaje genérico, lo que causa que los agentes pierdan el contexto de la tarea.
🔍 Síntomas
Comportamiento observable
Cuando una llamada al modelo falla o se agota el tiempo de espera y OpenClaw activa un reintento de respaldo, el agente recibe solo un mensaje genérico en lugar de la instrucción de tarea original. Esto se manifiesta de dos formas distintas:
1. Evidencia en el registro de sesión
El registro de sesión muestra un mensaje de usuario sin metadatos del remitente que reemplaza la tarea original:
// Session log excerpt (session ec19b29b)
Line 25: { customType: "model-snapshot", provider: "xiaomi", model: "mimo-v2-pro" }
Line 26: { role: "user", content: "Continue where you left off. The previous model attempt failed or timed out.", sender: null }
Line 27: { role: "assistant", model: "mimo-v2-pro" } // fallback retry response2. Pérdida de contexto en escenarios de subagente
Cuando se crea un subagente con una instrucción de tarea específica (por ejemplo, [Subagent Task]: RECORD時系列整列), la tarea original se descarta por completo:
// Original prompt sent to subagent
"[Subagent Task]: RECORD時系列整列"
// Prompt received after fallback retry
"Continue where you left off. The previous model attempt failed or timed out."El agente debe inferir la tarea solo del historial de sesión, lo que genera el riesgo de:
- Selección incorrecta de acciones basada en contexto incompleto
- Error al completar la tarea prevista
- Posible generación de salidas no deseadas
3. Indicadores de diagnóstico
El problema puede identificarse examinando el comportamiento de la función resolveFallbackRetryPrompt():
// Current implementation (dist/agent-command-*.js)
function resolveFallbackRetryPrompt(params) {
if (!params.isFallbackRetry) return params.body;
if (!params.sessionHasHistory) return params.body;
return "Continue where you left off. The previous model attempt failed or timed out.";
}🧠 Causa raíz
Análisis técnico
La causa raíz radica en la lógica condicional de resolveFallbackRetryPrompt() que utiliza una estrategia de reemplazo de retorno anticipado en lugar de una estrategia de concatenación.
Secuencia de fallo:
- El agente envía `params.body` que contiene el prompt original del usuario al modelo
- La llamada al modelo falla o se agota el tiempo de espera (por ejemplo, xiaomi/mimo-v2-pro)
- OpenClaw establece `params.isFallbackRetry = true`
- OpenClaw verifica `params.sessionHasHistory = true` (la sesión contiene mensajes previos)
- La función ejecuta la ruta de reemplazo:
return "Continue where you left off. The previous model attempt failed or timed out."; - El `params.body` original se descarta por completo
Inconsistencia arquitectónica:
La función confunde dos requisitos distintos:
- Requisito A: Notificar al modelo que se produjo un reintento
- Requisito B: Preservar la instrucción de tarea original
La implementación actual satisface el Requisito A mientras viola completamente el Requisito B.
Análisis de la ruta del código:
javascript // In runAgentAttempt(): const effectivePrompt = resolveFallbackRetryPrompt({ body: params.body, isFallbackRetry: params.isFallbackRetry, sessionHasHistory: params.sessionHasHistory });
Cuando isFallbackRetry es true y sessionHasHistory es true, la función devuelve una cadena fija en lugar de combinarla con params.body.
Cadena de impacto:
Original Prompt (params.body)
↓
Model Call Fails
↓
isFallbackRetry = true
↓
sessionHasHistory = true
↓
resolveFallbackRetryPrompt() returns FIXED_STRING
↓
effectivePrompt = FIXED_STRING ← Original task LOST
↓
Fallback model receives no task context🛠️ Solución paso a paso
Estrategia de resolución
Modificar resolveFallbackRetryPrompt() para anteponer la notificación de reintento al prompt original en lugar de reemplazarlo.
Cambio de código
Archivo: dist/agent-command-*.js (varía según la versión)
Antes:
function resolveFallbackRetryPrompt(params) {
if (!params.isFallbackRetry) return params.body;
if (!params.sessionHasHistory) return params.body;
return "Continue where you left off. The previous model attempt failed or timed out.";
}Después:
function resolveFallbackRetryPrompt(params) {
if (!params.isFallbackRetry) return params.body;
if (!params.sessionHasHistory) return params.body;
return "[System: Previous model attempt failed or timed out. Continuing from where you left off.]\n\n" + params.body;
}Implementación alternativa (más detallada)
Para entornos que requieren una separación más clara:
function resolveFallbackRetryPrompt(params) {
if (!params.isFallbackRetry) return params.body;
if (!params.sessionHasHistory) return params.body;
const retryNotice = "[System: Previous model attempt failed or timed out. Continuing from where you left off.]\n\n";
const originalPrompt = params.body;
return retryNotice + originalPrompt;
}Pasos de despliegue
- Localizar el archivo afectado:
find /path/to/openclaw -name "agent-command-*.js" -type f - Respaldar el archivo original:
cp /path/to/agent-command-*.js /path/to/agent-command-*.js.bak - Aplicar la corrección usando sed:
sed -i 's/return "Continue where you left off. The previous model attempt failed or timed out.";/return "[System: Previous model attempt failed or timed out. Continuing from where you left off.]\\n\\n" + params.body;/g' /path/to/agent-command-*.js - Verificar el cambio:
grep -A3 "function resolveFallbackRetryPrompt" /path/to/agent-command-*.js - Reiniciar el servicio OpenClaw:
sudo systemctl restart openclaw
🧪 Verificación
Metodología de verificación
Para confirmar la corrección, simula un fallo del modelo y verifica que el prompt de reintento de respaldo contenga la tarea original.
Procedimiento de prueba
1. Habilitar el registro de depuración:
export OPENCLAW_LOG_LEVEL=debug
export DEBUG=openclaw:agent:*2. Activar un escenario de respaldo:
Crear una configuración de agente de prueba con un modelo que falle intencionalmente:
// test-fallback-prompt.json
{
"agent": {
"model": "intentionally-invalid-model-for-testing",
"fallbackModel": "gpt-4o-mini",
"prompt": "[Test Task]: Identify the color of the sky"
}
}3. Ejecutar el agente:
openclaw run --config test-fallback-prompt.json --session test-fallback-$(date +%s)4. Inspect Session Log:
openclaw session log --session-id <session-id> --format json | jq '.messages[] | select(.role == "user") | {content, sender, metadata}'5. Verificar la salida esperada:
Antes de la corrección, la salida muestra:
{
"content": "Continue where you left off. The previous model attempt failed or timed out.",
"sender": null,
"metadata": {}
}Después de la corrección, la salida debería mostrar:
{
"content": "[System: Previous model attempt failed or timed out. Continuing from where you left off.]\n\n[Test Task]: Identify the color of the sky",
"sender": "system",
"metadata": {
"isFallbackRetry": true
}
}Script de verificación automatizada
#!/bin/bash
SESSION_ID=$(openclaw session list --limit 1 --format json | jq -r '.[0].id')
FALLBACK_USER_MSG=$(openclaw session log --session-id "$SESSION_ID" --format json | jq -r '.messages[] | select(.role == "user" and .sender == null) | .content')
if echo "$FALLBACK_USER_MSG" | grep -q "\[System: Previous model attempt"; then
if echo "$FALLBACK_USER_MSG" | grep -q "\[Test Task\]"; then
echo "✅ VERIFIED: Original prompt preserved in fallback retry"
exit 0
else
echo "❌ FAILED: System message present but original prompt missing"
exit 1
fi
else
echo "❌ FAILED: Generic message still being used (fix not applied)"
exit 1
fi⚠️ Errores comunes
Casos extremos y trampas específicas del entorno
1. Cuerpo de prompt vacío (params.body es una cadena vacía)
Si params.body es una cadena vacía, la implementación corregida producirá un prompt con solo el aviso del sistema:
// Result when params.body = ""
"[System: Previous model attempt failed...]\n\n"
// ← Empty original task (may be valid if session history is sufficient)Mitigación: Verificar que params.body no esté vacío antes de la concatenación:
function resolveFallbackRetryPrompt(params) {
if (!params.isFallbackRetry) return params.body;
if (!params.sessionHasHistory) return params.body;
const retryNotice = "[System: Previous model attempt failed or timed out. Continuing from where you left off.]\n\n";
return params.body ? retryNotice + params.body : retryNotice.trim();
}2. Prompts originales muy largos
Los prompts largos concatenados con el aviso de reintento pueden exceder los límites de contexto del modelo.
- Monitorear el uso de tokens cuando los prompts originales se aproximen a los límites del modelo
- Considerar truncar `params.body` si excede un umbral (por ejemplo, 4000 tokens)
3. Caracteres no ASCII en el prompt original
Los caracteres japoneses (como en el problema reportado con RECORD時系列整列) deben conservarse correctamente:
// Verify encoding is maintained
const testPrompt = "[Subagent Task]: RECORD時系列整列";
const fixed = "[System: ...]\n\n" + testPrompt;
console.log(fixed.includes("RECORD時系列整列")); // Must be true4. Almacenamiento en caché de contenedores Docker
Si OpenClaw se está ejecutando en Docker, los archivos JavaScript en caché pueden persistir:
# Rebuild container to ensure new code is deployed
docker-compose down
docker-compose build --no-cache openclaw
docker-compose up -d5. Reintentos de respaldo secuenciales múltiples
Si un modelo falla varias veces seguidas, cada reintento puede anteponer avisos del sistema adicionales:
// After 3 retries, prompt becomes:
"[System: ...]\n\n[System: ...]\n\n[System: ...]\n\n[Test Task]: ..."
// ← Duplicate notices accumulateMitigación: Verificar si el prompt original ya contiene el aviso de reintento antes de anteponer:
function resolveFallbackRetryPrompt(params) {
if (!params.isFallbackRetry) return params.body;
if (!params.sessionHasHistory) return params.body;
if (params.body.includes("[System: Previous model attempt")) return params.body;
return "[System: Previous model attempt failed...]\n\n" + params.body;
}6. Compatibilidad de versiones
La firma de la función o el sitio de llamada pueden cambiar entre versiones:
| Versión | Patrón de archivo | Estado |
|---|---|---|
| 2026.4.9 | agent-command-8TL7BESJ.js | Afectado |
| 2026.4.11 | agent-command-BUw17dbz.js | Afectado |
Siempre verificar la ubicación exacta de la función y los nombres de los parámetros en tu versión implementada.
🔗 Errores relacionados
Problemas relacionados contextualmente
Los siguientes errores y problemas históricos están relacionados con el comportamiento del prompt de reintento de respaldo:
1. Errores de tiempo de espera del modelo
- E_TIMEOUT: La llamada al modelo excedió el límite de tiempo
- E_MODEL_UNAVAILABLE: El endpoint del modelo no es accesible
- E_RATE_LIMIT: Límite de tasa de API excedido durante el intento de respaldo
Estos errores activan el indicador isFallbackRetry, que activa la ruta de código problemática.
2. Errores de ventana de contexto
- E_CONTEXT_LENGTH: El prompt combinado + historial excede el límite de contexto del modelo
- Puede ocurrir después de la corrección si el aviso de reintento + prompt original + historial excede los límites
3. Problemas históricos
| ID del problema | Descripción | Estado |
|---|---|---|
| GH-XXXX | Reporte inicial: El subagente pierde el contexto de la tarea al reintentar | Abierto |
| GH-YYYY | Los registros de sesión muestran mensajes de usuario con metadatos de remitente nulos | Relacionado |
4. Parámetros de configuración relacionados
// These parameters control the fallback behavior
interface FallbackConfig {
enabled: boolean; // Enable/disable fallback retry
maxRetries: number; // Maximum retry attempts
retryDelay: number; // Delay between retries (ms)
retryModels: string[]; // List of fallback models to try
preserveOriginalPrompt: boolean; // NEW: Flag to preserve original prompt
}5. Patrones similares en el código base
Otras funciones que pueden presentar problemas similares de reemplazo vs. concatenación:
- `resolveSystemPrompt()` - Puede sobrescribir las instrucciones del sistema
- `injectContextSummary()` - Podría reemplazar en lugar de agregar contexto
- `formatHistoryForModel()` - Podría truncar el historial en lugar de resumirlo
6. Recomendaciones de monitoreo
Implementar telemetría para escenarios de reintento de respaldo:
// Suggested metrics to track
metrics.increment('openclaw.fallback.retry.count');
metrics.gauge('openclaw.fallback.prompt.length', effectivePrompt.length);
metrics.histogram('openclaw.fallback.prompt.original_length', params.body.length);