#user turns pile up
1 messages · Page 1 of 1 (latest)
Yep â donât pass consecutive user turns through raw to a fragile backend.
OpenAI-compatible APIs accept a messages array, but if your custom endpoint is translating that into a local/chat-template model, you should normalize at the endpoint boundary. OpenAIâs docs describe multi-turn messages by role, but proxy/backends vary a lot in how well they handle odd role ordering. (platform.openai.com)
Best fix:
function mergeConsecutiveUserMessages(messages) {
const out = [];
for (const msg of messages) {
const prev = out[out.length - 1];
if (msg.role === "user" && prev?.role === "user") {
prev.content += `\n\n[Additional user message]\n${msg.content}`;
} else {
out.push({ ...msg });
}
}
return out;
}
Even better for custom endpoints: render the active burst as one final user message:
{
"role": "user",
"content": "The user sent these messages in order:\n\n1. ...\n2. ...\n\nRespond to the latest intent."
}
If this is OpenClaw traffic specifically, also check:
openclaw --versionfirst- queue mode:
/queue collectbatches rapid messages into one reply;/queue followupprocesses one at a time - docs: https://docs.openclaw.ai/help/faq
But architecturally: custom OpenAI-compatible endpoint should be defensive. Never assume incoming role order is model-safe; normalize/merge before applying the backend chat template.
wait really? is that what the official api's do? i dont think so. shouldnt it be something that openclaw should fix
Youâre right to push back â I overstated that.
Official APIs generally donât require OpenClaw/custom endpoints to invent an assistant turn. OpenAIâs chat format is just a messages array with roles; Anthropic explicitly says consecutive user / assistant messages are combined instead of erroring. (help.openai.com)
So yes: if OpenClaw is sending adjacent user turns to an OpenAI-compatible provider and that provider/model breaks badly, thatâs probably an OpenClaw compat bug/gap, not something you should have to solve only in your endpoint.
I checked source: OpenClaw already has a repair path âto prevent consecutive user turnsâ in normal session handling, and /queue collect is the documented default for batching rapid messages. But the OpenAI-compatible provider transport appears to pass converted messages through, so there may be a path that bypasses/doesnât fully normalize this.
Workaround for now:
- try
/queue collect debounce:2s - or endpoint-side merge consecutive
usermessages as a defensive guard
But please file this as an issue with:
openclaw --version- model/provider config shape
- redacted request payload showing consecutive user roles
Issue link: https://github.com/openclaw/openclaw/issues
Yeah â I checked current main, and this looks related to a real change from Apr 28, 2026: fix(messages): keep group replies tool-only by default.
For Discord guild/channel messages, group replies now default to:
messages: {
groupChat: {
visibleReplies: "message_tool"
}
}
In that tool-only mode, Discord ack/status reactions are suppressed, so your ackReactionScope: "group-all" wonât bring them back by itself.
To restore the old behavior:
messages: {
groupChat: {
visibleReplies: "automatic"
}
}
Caveat: that also restores legacy automatic visible replies in group/channel rooms, not just reactions.
Can you also paste your openclaw --version? If you want tool-only replies but still status reactions, that may need a GitHub issue because current main seems to couple those behaviors. Docs: https://docs.openclaw.ai/channels/groups
it also restores legacy automatic visible replies... what that mean? please dumb it down :) uwu
Dumbed down:
message_tool mode = the bot thinks privately first.
It only posts in the Discord channel if it explicitly uses the message tool.
automatic mode = old behavior.
If the model writes a normal final answer, OpenClaw posts that answer straight into the Discord channel automatically.
So this:
messages: {
groupChat: {
visibleReplies: "automatic"
}
}
means: âin Discord group/channel threads, let normal assistant replies show up publicly again.â
That also makes the reaction/status behavior come back, but it may make the bot chattier in channels. Tiny tradeoff gremlin đŠ
give me an example to illustrate?
Example:
Someone says in a Discord channel:
âKrill, summarize this threadâ
With visibleReplies: "message_tool"
The model may internally produce:
âHereâs the summaryâŠâ
âŠbut OpenClaw does not automatically post that.
The bot only speaks if it explicitly calls:
message.send("Hereâs the summary...")
So itâs quieter / more intentional.
With visibleReplies: "automatic"
The modelâs normal final answer:
âHereâs the summaryâŠâ
gets posted directly into Discord automatically, like older behavior.
So basically:
message_tool= âonly speak if you deliberately use the send-message toolâ
automatic= âwhatever final answer the assistant writes becomes a Discord messageâ
hmm then it sounds like a bad solution to the issue.
Yeah, I agree â for your goal, automatic is more of a workaround than a good fix.
What you want is:
âKeep group replies tool-only / quiet, but still show ack/status reactions so I know the agent is working.â
Current main seems to suppress reactions when Discord group/channel replies are in message_tool mode. So the clean fix would be upstream: decouple status reactions from visible reply mode.
Best options:
- Short-term: use
visibleReplies: "automatic"only if youâre okay with chattier public replies. - Better: stay on
message_toolmode and file a GitHub issue asking for status reactions to work there too: https://github.com/openclaw/openclaw/issues
Include your openclaw --version and that config snippet. Iâd phrase it as: âDiscord messages.statusReactions stopped showing when group visible replies defaulted to message_tool on main.â
Oof â this looks like a half-stale source build, not your config exploding.
Plain English: your wrapper is running:
/home/ubuntu/openclaw/dist/cli/run-main.js
and that built file expects this generated chunk:
dist/memory-state-C9pEfHRa.js
âŠbut that chunk isnât there. So dist/ is mismatched after the update.
Try a clean rebuild from the checkout:
cd /home/ubuntu/openclaw
mv dist "dist.broken.$(date +%Y%m%d-%H%M%S)" 2>/dev/null || true
pnpm install
pnpm build
pnpm openclaw --version
pnpm openclaw gateway restart
If pnpm build fails, paste the first real error above the stack trace.
That guard is actually protecting you.
It means:
- your config was last touched by OpenClaw 2026.4.27
- the binary you just ran is OpenClaw 2026.4.26
- so OpenClaw refuses to restart with the older code
Most likely you now have two installs / stale PATH / stale checkout.
Run:
which -a openclaw
openclaw --version
cd /home/ubuntu/openclaw
git status --short
git branch --show-current
git pull --ff-only
pnpm install
pnpm build
node dist/entry.js --version
You want that last version to be 2026.4.27 or newer. Then restart using the rebuilt one:
node /home/ubuntu/openclaw/dist/entry.js gateway install --force
node /home/ubuntu/openclaw/dist/entry.js gateway restart
Donât use OPENCLAW_ALLOW_OLDER_BINARY_DESTRUCTIVE_ACTIONS=1 unless youâre intentionally downgrading. This doesnât look like that â it looks like a stale binary mismatch.
Ah, found it: youâre in a detached git checkout.
That means /home/ubuntu/openclaw is sitting on a specific old commit, not on main, so git pull doesnât know what branch to update. Your wrapper is still running 2026.4.26.
Try:
cd /home/ubuntu/openclaw
git fetch origin
git switch main || git switch --track -c main origin/main
git pull --ff-only origin main
pnpm install
pnpm build
node dist/entry.js --version
That version should say 2026.4.27 or newer. Then: