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
    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