openclaw - ✅(Solved) Fix Kimi K2.5 streaming event order error: message_start before message_stop [2 comments, 2 participants]

Official PRs (…)
ON THIS PAGE

Recommended Tools

×6

Utilities matched from this issue’s tags and category — try them while you read without losing context.

GitHub issue graph ai analysis

Paste a GitHub issue URL. We fetch that issue, discover linked issues from bodies/comments/timeline, collect linked pull requests, and produce a structured English report.

The report is written in English Markdown for sharing and archival.

Helpful · Quick feedback

Loading…
GitHub stats
openclaw/openclaw#44482Fetched 2026-04-08 00:46:21
View on GitHub
Comments
2
Participants
2
Timeline
6
Reactions
1
Author
Timeline (top)
commented ×2closed ×1locked ×1subscribed ×1

Error Message

Error Messages

  1. ~80% of runs fail with streaming event order error
  • Error persists even with blocking mode enabled

PR fix notes

Fix: Kimi K2.5 Cron Jobs Fail with SSE Stream Event Order Errors

Problem

Cron jobs using kimi-coding/k2p5 via OpenRouter fail with ~80% frequency due to out-of-order SSE events in the streaming response parser:

Unexpected event order, got message_start before receiving "message_stop"
Unexpected event order, got content_block_stop before "message_start"

Setting agents.defaults.blockStreamingDefault: "on" does not resolve the issue — the error persists even in blocking mode, suggesting the stream parser does not fully flush or reset state between isolated cron sessions.


Workarounds

Option 1: Switch cron jobs to non-streaming mode (Recommended)

Force the cron payload to use blocking (non-streaming) requests by passing stream: false explicitly:

{
  "id": "b393d508-ec9a-4b6d-bb97-7f584eb0ef83",
  "name": "fpl-daily-check",
  "schedule": { "kind": "cron", "expr": "0 12 * * *", "tz": "Europe/London" },
  "sessionTarget": "isolated",
  "payload": {
    "kind": "agentTurn",
    "message": "Run FPL check...",
    "model": "kimi-coding/k2p5",
    "stream": false
  }
}

If stream: false is not yet a supported payload field, fall back to Option 2.


Option 2: Add retry logic with exponential backoff

Wrap cron execution with a retry shell script to absorb the ~80% failure rate until a proper fix lands:

#!/bin/bash
# retry-cron.sh
JOB_ID="b393d508-ec9a-4b6d-bb97-7f584eb0ef83"
MAX_RETRIES=5
DELAY=10

for i in $(seq 1 $MAX_RETRIES); do
  echo "Attempt $i..."
  openclaw cron run --job-id "$JOB_ID" --mode force && exit 0
  echo "Failed. Retrying in ${DELAY}s..."
  sleep $DELAY
  DELAY=$((DELAY * 2))
done

echo "All $MAX_RETRIES attempts failed."
exit 1

Update your crontab to call the script instead of openclaw directly:

0 12 * * * /home/user/retry-cron.sh >> /var/log/openclaw-cron.log 2>&1

Option 3: Switch to a more stable provider for cron jobs

If Kimi K2.5 is not strictly required for these workflows, use Anthropic or OpenAI models which have stable SSE implementations:

{
  "payload": {
    "kind": "agentTurn",
    "message": "Run FPL check...",
    "model": "anthropic/claude-sonnet-4-6"
  }
}

Use Kimi K2.5 for interactive sessions where a retry is trivial, and reserve cron jobs for providers with reliable stream ordering.


Option 4: Defensive stream parser patch (Advanced)

If you are running a self-hosted or patched build, add state reset logic to the SSE parser so it recovers gracefully when message_start arrives before message_stop:

// stream-parser.ts (pseudocode — adapt to actual source)
function handleEvent(event: SSEEvent) {
  if (event.type === "message_start" && this.state !== "idle") {
    // Kimi K2.5 may send message_start without prior message_stop
    console.warn("Stream reset: message_start received before message_stop — resetting parser state")
    this.reset() // flush current state before processing new message
  }

  if (event.type === "content_block_stop" && this.state === "idle") {
    // Discard orphaned content_block_stop events
    console.warn("Stream warning: content_block_stop received before message_start — discarding")
    return
  }

  this.processEvent(event)
}

Verification

After applying Option 1 or Option 2:

# Force-run the job 5 times and check failure rate
for i in {1..5}; do
  openclaw cron run --job-id b393d508-ec9a-4b6d-bb97-7f584eb0ef83 --mode force
  echo "---"
done

A successful fix should reduce the failure rate from ~80% to 0%.


Upstream Fix Required

The SSE normalisation layer needs the same hardening applied for reasoning_content in #38669 / #39907 / #40552:

  1. Tolerate message_start without prior message_stop — reset parser state and continue
  2. Discard orphaned content_block_stop events — log a warning, do not throw
  3. Add provider-specific SSE quirk flags — allow per-provider tolerances in openclaw.json

Suggested config once implemented:

{
  "providers": {
    "kimi-coding": {
      "sseQuirks": {
        "tolerateOutOfOrderEvents": true
      }
    }
  }
}

Environment

FieldValue
OpenClaw version2026.3.11
Modelkimi-coding/k2p5via OpenRouter
Session targetisolated
OSUbuntu 22.04.5 LTS (Hetzner VPS)
Failure rate~80%
Consecutive failures16
Affected workflowsT212, FPL, email summaries

References

  • Related fixes: #38669, #39907, #40552
  • blockStreamingDefault: "on" does not resolve this issue
  • Kimi K2.5 interactive sessions are unaffected — issue is specific to isolated cron sessions

Code Example

Unexpected event order, got message_start before receiving "message_stop"
Unexpected event order, got content_block_stop before "message_start"

---

{
  "id": "b393d508-ec9a-4b6d-bb97-7f584eb0ef83",
  "name": "fpl-daily-check",
  "schedule": {"kind": "cron", "expr": "0 12 * * *", "tz": "Europe/London"},
  "sessionTarget": "isolated",
  "payload": {
    "kind": "agentTurn",
    "message": "Run FPL check...",
    "model": "kimi-coding/k2p5"
  }
}
RAW_BUFFERClick to expand / collapse

Bug Description

Cron jobs using Kimi K2.5 (kimi-coding/k2p5) fail intermittently with streaming event parsing errors.

Error Messages

Unexpected event order, got message_start before receiving "message_stop"
Unexpected event order, got content_block_stop before "message_start"

Environment

  • OpenClaw version: 2026.3.11
  • Model: kimi-coding/k2p5 (Kimi K2.5 via OpenRouter)
  • Session target: isolated (cron jobs)
  • OS: Ubuntu 22.04.5 LTS (Hetzner VPS)

Reproduction Steps

  1. Create a cron job with sessionTarget: isolated and model kimi-coding/k2p5
  2. Run the job (force run with openclaw cron run --job-id <id> --mode force)
  3. ~80% of runs fail with streaming event order error

Example Job Config

{
  "id": "b393d508-ec9a-4b6d-bb97-7f584eb0ef83",
  "name": "fpl-daily-check",
  "schedule": {"kind": "cron", "expr": "0 12 * * *", "tz": "Europe/London"},
  "sessionTarget": "isolated",
  "payload": {
    "kind": "agentTurn",
    "message": "Run FPL check...",
    "model": "kimi-coding/k2p5"
  }
}

Attempted Fixes

  • Updated to 2026.3.11 (which fixed the reasoning_content mismatch)
  • Set agents.defaults.blockStreamingDefault: "on" — did not resolve
  • Error persists even with blocking mode enabled

Impact

  • 16 consecutive failures on hourly cron jobs
  • All isolated cron jobs with Kimi K2.5 affected
  • Prevents automated workflows (T212, FPL, email summaries)

Related

Likely related to Anthropic-format tool calling fixes in 2026.3.11 (#38669, #39907, #40552) — may need similar hardening for SSE event stream ordering.

extent analysis

Fix Plan

To resolve the intermittent streaming event parsing errors in cron jobs using Kimi K2.5, we will implement a custom event ordering check and handling mechanism.

Step-by-Step Solution

  1. Update the cron job configuration: Add a custom eventHandler script to the job config that will handle event ordering checks.
  2. Implement event ordering checks: Create a script that checks the event order and handles any discrepancies.
  3. Use a temporary buffer: Implement a temporary buffer to store events and ensure correct ordering.

Example Code

import json

def event_handler(events):
    # Initialize an empty buffer
    buffer = []
    
    # Iterate over the events
    for event in events:
        # Check if the event is a message_start
        if event['type'] == 'message_start':
            # If the buffer is not empty, handle the previous events
            if buffer:
                handle_events(buffer)
                buffer = []
            # Add the message_start event to the buffer
            buffer.append(event)
        # Check if the event is a content_block_stop
        elif event['type'] == 'content_block_stop':
            # If the buffer is empty or the last event is not a message_start, handle the error
            if not buffer or buffer[-1]['type'] != 'message_start':
                handle_error(event)
            else:
                # Add the content_block_stop event to the buffer
                buffer.append(event)
        # Check if the event is a message_stop
        elif event['type'] == 'message_stop':
            # If the buffer is empty or the last event is not a content_block_stop, handle the error
            if not buffer or buffer[-1]['type'] != 'content_block_stop':
                handle_error(event)
            else:
                # Add the message_stop event to the buffer and handle the events
                buffer.append(event)
                handle_events(buffer)
                buffer = []

def handle_events(events):
    # Handle the events in the correct order
    for event in events:
        print(event)

def handle_error(event):
    # Handle the error
    print(f"Error: {event}")

# Example usage
events = [
    {'type': 'message_start'},
    {'type': 'content_block_stop'},
    {'type': 'message_stop'}
]
event_handler(events)

Verification

To verify that the fix worked, run the cron job with the updated configuration and check the logs for any errors. If the events are being handled correctly, there should be no errors related to event ordering.

Extra Tips

  • Make sure to test the custom event handler script thoroughly to ensure it handles all possible event ordering scenarios.
  • Consider adding additional logging to help diagnose any issues that may arise.
  • If the issue persists, consider reaching out to the Kimi K2.5 support team for further assistance.

Vote matrix · Quick signals

Works
Did the solution work? Tap to confirm.
Easy Fix
Was it a quick fix?
Time Saver
Did it save you time?
Blocking
Was it severely blocking?
Common Issue
Are others likely hitting this too?
Flaky / Intermittent
Is it intermittent?
Verified / Reproducible
Can you reproduce it reliably?
Loading…

Still need to ship something?

×6

Another batch ranked right after the header list — different links, same matching logic.

Back to top recommendations

TRENDING