Skip to content

[Bug] OpenClaw plugin assemble() produces consecutive user turns after archive commit — causes empty responses from Gemini/Anthropic #1724

@benediktkraus

Description

@benediktkraus

[Bug] OpenClaw plugin assemble() produces consecutive user turns after archive commit — causes empty responses from Gemini/Anthropic

Summary

After an OpenViking session archive commit, buildAssembledContext() in the OpenClaw context-engine plugin produces multiple consecutive user-role messages. This violates the strict alternating turn ordering required by Gemini and Anthropic APIs, causing stopReason=stop payloads=0 (empty model response) on every subsequent turn until the session is manually deleted.

Reproduction

  1. Send ~30-40 messages to an OpenClaw agent (Gemini Pro) with the OpenViking context-engine plugin active
  2. Wait for commitTokenThreshold to trigger automatic commit(wait=false) with archived=true
  3. Send another message after archive completes
  4. Agent returns: ⚠️ Agent couldn't generate a response. Please try again.
  5. Every subsequent message fails identically — the session is permanently broken

Root Cause

buildArchiveMemory() (context-engine.ts) creates archive context as role: "user" messages:

messages.push({ role: "user", content: `[Session History Summary]\n${archiveOverview}` });
messages.push({ role: "user", content: `[Archive Index]\n${lines.join("\n")}` });

buildAssembledContext() concatenates archive + session messages:

const assembled = [...archive.messages, ...session.messages];

After archive, if session.messages is empty or starts with a user message, the assembled array becomes:

user: [Session History Summary]...
user: [Archive Index]...
user: <new message>

Three consecutive user turns. sanitizeToolUseResultPairing() repairs tool-use/result pairing but does NOT merge consecutive user turns. mergeConsecutiveAssistants() exists for assistant turns but has no user counterpart.

The OpenClaw SDK provides validateGeminiTurns() and mergeConsecutiveUserTurns() in pi-embedded-helpers/turns.js, but the OV plugin does not call them, and the OC runtime only calls validateGeminiTurns in the replay path.

Gateway Log Pattern

openviking: committed session=XXX, status=accepted, archived=true
openviking: assemble entering session content for XXX: [{"role":"user","content":"[Session History Summary]..."},{"role":"user","content":"..."}]
empty response detected: runId=XXX provider=google/gemini-3.1-pro-preview — retrying 1/1
empty response retries exhausted: runId=XXX attempts=1/1
incomplete turn detected: runId=XXX stopReason=stop payloads=0 — surfacing error to user

Suggested Fix

Add mergeConsecutiveUsers() analogous to mergeConsecutiveAssistants(), call after sanitizeToolUseResultPairing().

Environment

  • OpenClaw: 2026.4.24, OpenViking: v0.3.12, Plugin: @openclaw/openviking@2026.4.34
  • Provider: Google Gemini 3.1 Pro

Workaround

Delete the session file and restart gateway. Recurs after next archive commit.

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

Status

Done

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions