OpenClaw’s Approval Flow Has a Routing Bug, Which Is Exactly Why Approval Gates Need End-to-End Semantics

OpenClaw’s Approval Flow Has a Routing Bug, Which Is Exactly Why Approval Gates Need End-to-End Semantics

Approval gates are supposed to be the part of an agent platform where everyone can finally relax. The model wants to run something risky, the runtime pauses, the human approves or denies, and the system continues under supervision. OpenClaw issue #96103 is a useful reminder that this picture is too neat. Approval is not a button. It is a routed, asynchronous, cross-channel state machine — and if the final answer goes to the wrong place, the security control has only done half its job.

The bug report is narrow and concrete. An operator running OpenClaw 2026.6.9 with the external @lansenger-pm/openclaw-lansenger-channel plugin sees native approval cards work through the first half of the flow. A user sends a non-whitelisted command in a Lansenger DM. OpenClaw raises approval-pending. The plugin sends an approval card. The user submits /approve <id> allow-once. The runtime resolves the approval and even sends the acknowledgement — “Approval allow-once submitted” — back to the original Lansenger DM.

Then the important part disappears. The agent continuation runs, but the actual post-approval answer routes to webchat instead of the channel where the user asked and approved. According to the issue, the sequence includes exec.approval.resolve, a successful acknowledgement delivery, and then an agent.wait 2914ms with no further Lansenger outbound. The reporter points at missing routing fields on the injected approval-resolution context: turnSourceTo, turnSourceChannel, and turnSourceAccountId. A near-duplicate issue, #96102, was filed one minute earlier with the same failure class: agent replies land in webchat instead of the original channel.

The approval decision is not the whole approval flow

The industry talks about human-in-the-loop systems as if the “human” part is a single boolean. Allowed or denied. That is a comforting abstraction and a bad implementation model. In a real channel runtime, approval has at least five separate obligations: preserve the pending action, represent it accurately to the user, resolve the user’s decision, execute only what was approved, and deliver the resulting continuation back into the same conversation contract.

#96103 appears to satisfy the middle of that chain while losing the end. That matters. If a user approves a command in a DM and the answer is generated somewhere else, the platform has broken the supervision loop. The human does not know whether the command ran, whether it failed, whether it produced sensitive output, or whether the agent is still waiting. Worse, because the acknowledgement arrived correctly, the user gets a false sense that the runtime completed the supervised path.

That is why this is not just a Lansenger plugin bug or a cosmetic delivery issue. Approval gates are only meaningful when policy, execution, and delivery share the same identity. The original request has a channel, account, recipient, thread, session, permission context, and probably a user expectation about privacy. The approval continuation must carry those attributes forward. Treating it like a generic injected system tick is how a platform accidentally splits “approved action” from “visible result.”

OpenClaw is finding the same bug shape in multiple subsystems

The timing is useful because OpenClaw just shipped related approval-plane hardening in PR #94545, included in the v2026.6.10-beta.2 release notes. That fix dealt with a different split-brain problem: trusted-policy lookup could read from the wrong registry path while hooks remained active through a composed registry. The patch preserved bundled-before-installed policy ordering and added 98 focused tests. Different subsystem, same architectural smell: one piece of the runtime had a different view of “truth” than another piece.

#96103 rhymes with that earlier fix. In #94545, the live hook runner and trusted-policy resolver needed to agree on the tool/policy registry. In #96103, the approval resolver and delivery router need to agree on the original conversation target. Both are control-plane consistency problems, not intelligence problems. No better model will fix missing route metadata. No prompt will reliably reconstruct turnSourceTo after the platform drops it.

This is the practical edge of “agent security.” The market often reduces agent safety to model refusals, permission prompts, and sandboxing. Those are necessary, but they do not cover the boring state transitions where real systems fail. Approvals cross plugin boundaries. Plugin boundaries cross channel abstractions. Channel abstractions cross user identity. Every crossing is an opportunity to lose metadata that used to be implicit in a web UI.

What practitioners should do now

If you run OpenClaw or any comparable agent platform with external channel plugins, test approvals where your users actually live. Do not stop at webchat. Exercise allow-once, deny, timeout, command execution, post-approval synthesis, and final delivery in Slack, Telegram, Discord, email, internal chat, and any custom channel. The test should not merely assert that the command executed. It should assert that the continuation arrived in the original conversation and that the runtime can prove which route it used.

For plugin authors, the useful logging is not “approval resolved.” It is “approval resolved for original route X, continuation generated for route X, send attempted through provider Y, delivery acknowledged or failed.” Generated-but-undelivered assistant messages should be treated like failed tool calls: visible, queryable, and noisy enough to debug. A message that exists in an internal transcript but not in the user’s channel is not success. It is a delivery incident wearing a success costume.

For platform maintainers, approval-resolution context should be a typed contract, not a best-effort bag of fields. The continuation should require original channel identity, account identity, thread/recipient identity, and delivery policy unless the flow explicitly changes surfaces. If the runtime cannot reconstruct those values, it should fail closed with a visible error in the approving channel rather than silently dumping the answer into webchat.

The deeper lesson is that agent platforms need end-to-end semantics for control-plane features. “Approval” is not a UI component. It is a promise that the thing the user approved is the thing that ran, in the context where the user approved it, with the result returned to the user who made the decision. Anything less is approval theater.

Sources: OpenClaw issue #96103, OpenClaw issue #96102, OpenClaw PR #94545, OpenClaw v2026.6.10-beta.2 release notes