Skip to content

[Bug]: Cron announce delivery posts to Slack thread instead of channel top-level when session has prior thread context #27751

@carrotRakko

Description

@carrotRakko

Summary

Cron jobs with delivery.mode: "announce", sessionTarget: "isolated", and explicit delivery.to (Slack channel ID) post results as thread replies instead of channel top-level messages. The isolated session inherits lastThreadId from prior conversation context, which leaks into the delivery path.

Steps to reproduce

  1. Have an agent converse in a Slack thread in channel C0XXXXXXXXX
  2. Session store records lastThreadId from that thread
  3. A cron job fires with this config:
{
  "sessionTarget": "isolated",
  "delivery": {
    "mode": "announce",
    "channel": "slack",
    "to": "C0XXXXXXXXX"
  }
}
  1. Observe where the result is posted

Expected behavior

The cron result is posted as a top-level message in the specified channel.

Actual behavior

The cron result is posted as a thread reply to a previous conversation thread in the same channel. The delivery status shows success — OpenClaw believes it delivered correctly.

OpenClaw version

2026.2.25

Operating system

Amazon Linux 2023 (aarch64)

Install method

npm global

Logs, screenshots, and evidence

No error logs — delivery reports success. The message simply arrives in the wrong location: a thread reply instead of channel top-level.

Impact and severity

  • Affected: Any Slack user relying on cron announce delivery to a channel where the agent has prior thread context
  • Severity: Medium (cron results are hidden in unrelated threads, easy to miss)
  • Frequency: 100% repro when delivery.to matches the channel of a prior conversation thread
  • Consequence: Scheduled reports/notifications are buried in old threads instead of visible at channel top-level

Additional information

Root cause analysis:

Three layers combine to produce this bug:

  1. session.tsresolveCronSession(): When forceNew: true (isolated session), the new session entry is created via ...entry spread, which preserves lastThreadId, lastTo, lastChannel from the prior session. forceNew only resets sessionId, not delivery metadata.

  2. targets.tsresolveSessionDeliveryTarget(): When delivery.to matches lastTo (same channel), lastThreadId is adopted as threadId (line ~152: mode !== "heartbeat" && channel === lastChannel ? lastThreadId : undefined).

  3. delivery-dispatch.ts: useDirectDelivery is true when threadId != null (line ~391), bypassing the announce flow and sending via deliverViaDirect with thread_ts set.

The fix likely belongs in (1): resolveCronSession should clear lastThreadId (and possibly lastTo, lastAccountId) when forceNew: true, since an isolated session should not inherit prior delivery routing state.

Related issues: #24176, #25450 (different symptoms in the same cron delivery area)

✍️ Author: Claude Code with @carrotRakko (AI-written, human-approved)

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions