Skip to content

[Bug]: cron with sessionTarget=current still lowercases Matrix roomId in 2026.4.27 (regression of #71798 via MCP loopback path) #75261

@sk7n4k3d

Description

@sk7n4k3d

Bug type

Behavior bug (incorrect output/state without crash)

Beta release blocker

No

Summary

The fix from #71798 (commit e309fd485e, released in 2026.4.25) does not cover the case where mcp__openclaw__cron action=add with sessionTarget: "current" (and no explicit delivery) is invoked from a Claude CLI subprocess via the MCP loopback HTTP server. The created cron job still stores a lowercased Matrix room ID in delivery.to, causing the same M_UNKNOWN: non-create event for room of unknown version [500] error and infinite retry loop (deleteAfterRun: true never applies because runs always fail).

Reproduced on 2026.4.27 (latest stable, published 2026-04-29).

Steps to reproduce

  1. Run OpenClaw 2026.4.27 with the Matrix channel enabled.
  2. Configure an agent (e.g. main) that uses claude-cli as backend (liveSession: claude-stdio), so it spawns a claude subprocess that talks to the MCP loopback over HTTP.
  3. From a Matrix room with uppercase characters in the room ID (e.g. !c9p9XFWiH5Szl4yZiP:matrix.example), ask the agent to schedule a one-shot reminder (mcp__openclaw__cron action=add, sessionTarget: "current", no delivery).
  4. Inspect ~/.openclaw/cron/jobs.json for the created job.
  5. Wait for the cron to fire.

Expected behavior

After fix #71798, currentDeliveryContext.to should preserve the case-sensitive Matrix room ID and the cron delivery.to should be the canonical-cased ID (e.g. !c9p9XFWiH5Szl4yZiP:...). The scheduled message delivers successfully and deleteAfterRun: true removes the job after one run.

Actual behavior

The created cron job stores lowercased sessionTarget and delivery.to:

{
  "name": "ping-salon-22h37",
  "agentId": "main",
  "sessionKey": "agent:main:matrix:channel:!c9p9xfwih5szl4yzip:matrix.devlabz.eu",
  "schedule": {"kind": "at", "at": "2026-04-30T20:37:00.000Z"},
  "sessionTarget": "session:agent:main:matrix:channel:!c9p9xfwih5szl4yzip:matrix.devlabz.eu",
  "deleteAfterRun": true,
  "delivery": {
    "mode": "announce",
    "to": "!c9p9xfwih5szl4yzip:matrix.devlabz.eu",
    "channel": "matrix"
  }
}

Each run errors with M_UNKNOWN: MatrixError: [500] non-create event for room of unknown version and the job retries indefinitely (3 errors observed in 5 minutes, backoff schedule 30s → 60s → 5min → 15min → 60min). Because the run is never lastRunStatus: "ok", deleteAfterRun: true never triggers.

Worse: each failed cron run takes the embedded code path which uses provider=anthropic directly (not claude-cli subprocess). Combined with Anthropic's recent third-party app billing rejection (HTTP 400 Third-party apps now draw from your extra usage), every retry pollutes logs with cascading fallback failures across anthropic/claude-opus-4-7 → anthropic/claude-sonnet-4-6 → anthropic/claude-opus-4-6 → ollama-cloud/....

Hypothesis on root cause

The fix in e309fd485e introduces inferDeliveryFromContext(opts.currentDeliveryContext) in createCronTool, with currentDeliveryContext.to sourced from options.currentChannelId ?? options.agentTo (line ~8880 in openclaw-tools-*.js). The non-embedded code path wires this correctly.

In our case (MCP loopback HTTP from a Claude CLI subprocess in claude-stdio live session mode):

  • The cron tool is reached via the loopback HTTP server, not the in-process createCronTool instance. The HTTP path may instantiate the cron tool through a different wiring that does not populate currentDeliveryContext, falling back to inferDeliveryFromSessionKey(agentSessionKey) which uses the lowercased parseAgentSessionKey output (session-key-utils-*.js normalizeOptionalLowercaseString).
  • Or currentChannelId itself is already lowercased in the request context before reaching the loopback handler (e.g. derived from originTo of a session whose store entry has lowercased channelId).

OpenClaw version

2026.4.27

Operating system

Linux 7.0.0-1-cachyos (Arch CachyOS)

Install method

Global npm install (npm install -g openclaw@2026.4.27), running as systemd user unit on edge node.

Model

claude-cli/claude-opus-4-7 (primary), with liveSession: claude-stdio (Claude CLI subprocess, MCP loopback HTTP)

Provider / routing chain

Matrix room → agent mainclaude-cli backend (subprocess) → MCP loopback HTTP → mcp__openclaw__cron

Workaround

Force every cron creation that should deliver to a Matrix room to include delivery explicitly with the canonical-cased to:

{
  "delivery": {
    "mode": "announce",
    "channel": "matrix",
    "to": "!c9p9XFWiH5Szl4yZiP:matrix.devlabz.eu"
  }
}

Documented this as a hard rule in our agent's AGENTS.md so the LLM never relies on the implicit sessionTarget: "current" derivation for Matrix.

Logs, screenshots, and evidence

2026-04-30T22:38:36 [agent/embedded] embedded run agent end: runId=553822d6-... isError=true
  model=claude-opus-4-7 provider=anthropic
  error=LLM request rejected: Third-party apps now draw from your extra usage,
  not your plan limits.

2026-04-30T... [cron] last error:
  M_UNKNOWN: MatrixError: [500] non-create event for room of unknown version
  in !c9p9xfwih5szl4yzip:matrix.devlabz.eu
  (https://matrix.devlabz.eu/_matrix/client/v3/rooms/%21c9p9xfwih5szl4yzip%3Amatrix.devlabz.eu/send/m.room.message/...)

# jobs-state.json:
"state": {
  "lastRunStatus": "error",
  "lastStatus": "error",
  "consecutiveErrors": 3,
  "lastError": "M_UNKNOWN: MatrixError: [500] non-create event for room of unknown version in !c9p9xfwih5szl4yzip:matrix.devlabz.eu"
}

Suggested fix direction

Either:

  1. Ensure currentDeliveryContext is populated in the MCP loopback HTTP path the same way it is for the in-process tool (line 8876 in bundled openclaw-tools), by reading the room ID from the session store (origin.nativeChannelId is preserved with correct casing in ~/.openclaw/agents/<id>/sessions/sessions.json), or
  2. In inferDeliveryFromSessionKey, instead of using the lowercased agentSessionKey, look up the canonical channel ID from the session store using the parsed agentId/rest tuple — Matrix room IDs (and other case-sensitive provider IDs) should be re-canonicalized from the store rather than reconstructed from a lowercased session key.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions