April 28, 2026 β€’ Version: 2026.4.9

resolveFallbackRetryPrompt Discards Original User Prompt on Model Fallback Retry

When a model call fails and triggers fallback retry, the resolveFallbackRetryPrompt() function replaces the entire original user prompt with a generic message, causing agents to lose task context.

πŸ” Symptoms

Observable Behavior

When a model call fails or times out and OpenClaw triggers a fallback retry, the agent receives only a generic message instead of the original task instruction. This manifests in two distinct ways:

1. Session Log Evidence

The session log shows a user message with no sender metadata that replaces the original task:

// 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 response

2. Context Loss in Subagent Scenarios

When a subagent is spawned with a specific task instruction (e.g., [Subagent Task]: RECORDζ™‚η³»εˆ—ζ•΄εˆ—), the original task is completely discarded:

// 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."

The agent must infer the task from session history alone, risking:

  • Incorrect action selection based on incomplete context
  • Failure to complete the intended task
  • Potential generation of unintended outputs

3. Diagnostic Indicators

The issue can be identified by examining the resolveFallbackRetryPrompt() function behavior:

// 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.";
}

🧠 Root Cause

Technical Analysis

The root cause lies in the conditional logic of resolveFallbackRetryPrompt() which uses an early-return replacement strategy instead of a concatenation strategy.

Failure Sequence:

  1. The agent sends `params.body` containing the original user prompt to the model
  2. The model call fails or times out (e.g., xiaomi/mimo-v2-pro)
  3. OpenClaw sets `params.isFallbackRetry = true`
  4. OpenClaw checks `params.sessionHasHistory = true` (session contains prior messages)
  5. The function executes the replacement path:
    return "Continue where you left off. The previous model attempt failed or timed out.";
  6. The original `params.body` is discarded entirely

Architectural Inconsistency:

The function conflates two distinct requirements:

  • Requirement A: Notify the model that a retry occurred
  • Requirement B: Preserve the original task instruction

The current implementation satisfies Requirement A while completely violating Requirement B.

Code Path Analysis:

javascript // In runAgentAttempt(): const effectivePrompt = resolveFallbackRetryPrompt({ body: params.body, isFallbackRetry: params.isFallbackRetry, sessionHasHistory: params.sessionHasHistory });

When isFallbackRetry is true and sessionHasHistory is true, the function returns a fixed string instead of combining it with params.body.

Impact Chain:

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

πŸ› οΈ Step-by-Step Fix

Resolution Strategy

Modify resolveFallbackRetryPrompt() to prepend the retry notification to the original prompt rather than replacing it.

Code Change

File: dist/agent-command-*.js (varies by version)

Before:

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.";
}

After:

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;
}

Alternative Implementation (More Verbose)

For environments requiring clearer separation:

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;
}

Deployment Steps

  1. Locate the affected file:
    find /path/to/openclaw -name "agent-command-*.js" -type f
  2. Backup the original file:
    cp /path/to/agent-command-*.js /path/to/agent-command-*.js.bak
  3. Apply the fix using 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
  4. Verify the change:
    grep -A3 "function resolveFallbackRetryPrompt" /path/to/agent-command-*.js
  5. Restart the OpenClaw service:
    sudo systemctl restart openclaw

πŸ§ͺ Verification

Verification Methodology

To confirm the fix, simulate a model failure and verify the fallback retry prompt contains the original task.

Test Procedure

1. Enable Debug Logging:

export OPENCLAW_LOG_LEVEL=debug
export DEBUG=openclaw:agent:*

2. Trigger a Fallback Scenario:

Create a test agent configuration with an intentionally failing model:

// 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. Execute the Agent:

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. Verify Expected Output:

Before fix, the output shows:

{
  "content": "Continue where you left off. The previous model attempt failed or timed out.",
  "sender": null,
  "metadata": {}
}

After fix, the output should show:

{
  "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
  }
}

Automated Verification Script

#!/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

⚠️ Common Pitfalls

Edge Cases and Environment-Specific Traps

1. Empty Prompt Body (params.body is empty string)

If params.body is an empty string, the fixed implementation will produce a prompt with only the system notice:

// Result when params.body = ""
"[System: Previous model attempt failed...]\n\n"
// ← Empty original task (may be valid if session history is sufficient)

Mitigation: Verify that params.body is non-empty before concatenation:

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. Very Long Original Prompts

Long prompts concatenated with the retry notice may exceed model context limits.

  • Monitor token usage when original prompts approach model limits
  • Consider truncating `params.body` if it exceeds a threshold (e.g., 4000 tokens)

3. Non-ASCII Characters in Original Prompt

Japanese characters (as in the reported issue with RECORDζ™‚η³»εˆ—ζ•΄εˆ—) must be properly preserved:

// Verify encoding is maintained
const testPrompt = "[Subagent Task]: RECORDζ™‚η³»εˆ—ζ•΄εˆ—";
const fixed = "[System: ...]\n\n" + testPrompt;
console.log(fixed.includes("RECORDζ™‚η³»εˆ—ζ•΄εˆ—")); // Must be true

4. Docker Container Caching

If OpenClaw is running in Docker, cached JavaScript files may persist:

# Rebuild container to ensure new code is deployed
docker-compose down
docker-compose build --no-cache openclaw
docker-compose up -d

5. Multiple Sequential Fallback Retries

If a model fails multiple times in succession, each retry may prepend additional system notices:

// After 3 retries, prompt becomes:
"[System: ...]\n\n[System: ...]\n\n[System: ...]\n\n[Test Task]: ..."
// ← Duplicate notices accumulate

Mitigation: Check if original prompt already contains the retry notice before prepending:

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. Version Compatibility

The function signature or call site may change between versions:

VersionFile PatternStatus
2026.4.9agent-command-8TL7BESJ.jsAffected
2026.4.11agent-command-BUw17dbz.jsAffected

Always verify the exact function location and parameter names in your deployed version.

Contextually Connected Issues

The following errors and historical issues are related to the fallback retry prompt behavior:

1. Model Timeout Errors

  • E_TIMEOUT: Model call exceeded time limit
  • E_MODEL_UNAVAILABLE: Model endpoint unreachable
  • E_RATE_LIMIT: API rate limit exceeded during fallback attempt

These errors trigger the isFallbackRetry flag, which activates the problematic code path.

2. Context Window Errors

  • E_CONTEXT_LENGTH: Combined prompt + history exceeds model context limit
  • May occur after fix if retry notice + original prompt + history exceeds limits

3. Historical Issues

Issue IDDescriptionStatus
GH-XXXXInitial report: Subagent loses task context on retryOpen
GH-YYYYSession logs show user messages with null sender metadataRelated

4. Related Configuration Parameters

// 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. Similar Patterns in Codebase

Other functions that may exhibit similar replacement-vs-concatenation issues:

  • `resolveSystemPrompt()` - May overwrite system instructions
  • `injectContextSummary()` - Could replace rather than append context
  • `formatHistoryForModel()` - Might truncate history instead of summarizing

6. Monitoring Recommendations

Implement telemetry for fallback retry scenarios:

// 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);

Evidence & Sources

This troubleshooting guide was automatically synthesized by the FixClaw Intelligence Pipeline from community discussions.