openclaw - ✅(Solved) Fix [Bug]: cron create silently accepts invalid delivery accountId without validation [2 pull requests]

Official PRs (…)
ON THIS PAGE

Recommended Tools

×6

Utilities matched from this issue’s tags and category — try them while you read without losing context.

GitHub issue graph ai analysis

Paste a GitHub issue URL. We fetch that issue, discover linked issues from bodies/comments/timeline, collect linked pull requests, and produce a structured English report.

The report is written in English Markdown for sharing and archival.

Helpful · Quick feedback

Loading…

openclaw cron create --announce --account <id> does not validate the accountId against configured channel accounts. A cron job created with a nonexistent account name is silently accepted and stored with the bogus value. At delivery time, this would cause a silent failure or misrouted delivery.

Error Message

  • Reject the job with an error: "Account 'nonexistent_bot_xyz' is not a configured Telegram account"
  • Warn and refuse to persist the job No error, no warning, no rejection. Both jobs were cleaned up after the test.
  • If not found, return an error and do not create the job

Root Cause

This is a LOW-severity bug because:

  • Most operators create cron jobs with known account names
  • The bug only manifests at delivery time, not creation time
  • Existing jobs with correct accountIds are not affected
  • There is no data loss — only missed or misrouted deliveries

Fix Action

Fix / Workaround

Current Workaround

PR fix notes

PR #69518: fix(cron): validate delivery accountId against configured channel accounts

Description (problem / solution / changelog)

Summary

Closes #69513.

openclaw cron create --announce --account <id> silently accepted any non-empty string as delivery.accountId and persisted it to cron/jobs.json, leading to silent failures or misrouted deliveries at run time when the accountId did not match any configured channel account.

This PR extends the existing create/update validation in src/gateway/server-methods/cron.ts to cross-check delivery.accountId (and delivery.failureDestination.accountId) against cfg.channels.<channel>.accounts at cron.add and cron.update time, symmetric to how delivery.channel is already validated there.

Root cause

  • assertValidCronAnnounceDelivery in src/gateway/server-methods/cron.ts validated only the channel id, never the accountId.
  • delivery-field-schemas.ts only shape-checks accountId as a non-empty trimmed string.
  • The delivery path in src/cron/isolated-agent/delivery-target.ts trusts an explicit jobPayload.accountId verbatim ("job.delivery.accountId takes highest precedence — explicitly set by the job author"), so an unknown accountId survives all the way to the outbound send and either misroutes or fails silently.

Fix

Add assertConfiguredAnnounceAccount(...) alongside the existing assertConfiguredAnnounceChannel(...) helper and call it for both delivery and delivery.failureDestination inside assertValidCronAnnounceDelivery. The rejection message mirrors the existing channel-level style:

delivery.accountId must be one of: other, sentry

Because assertValidCronUpdateDelivery already recomputes the next job via applyJobPatch and then calls assertValidCronAnnounceDelivery, the same guard applies to cron.update patches without additional wiring.

Why the fix is safe

  • Conservative and backwards compatible. Validation only triggers when the channel has an explicit channels.<id>.accounts map with at least one entry AND a non-empty accountId is provided. Flat single-account setups (channels.telegram.botToken = ... without an accounts map) keep working unchanged, and omitting accountId remains valid.
  • No config default changes. No schema, generated-metadata, help, or doctor surface is touched; nothing in docs/config/** or docs/.generated/** drifts.
  • No cross-surface coupling. The guard lives in the gateway handler file that already owns the sibling channel-level validator; no plugin SDK, no channel runtime, no normalize layer is affected.
  • Matches existing idioms. Uses normalizeMessageChannel (already imported) so canonicalization stays consistent with assertConfiguredAnnounceChannel. Returns one of: <sorted list> using Object.keys(accounts).toSorted() for deterministic output.
  • Sibling mode paths (none, webhook, last sentinel) are explicitly skipped, matching the announce-only scope of the existing validator.

Security / runtime controls unchanged

  • Runtime-enforced, not prompt-enforced. The check runs inside the cron.add / cron.update gateway request handlers before context.cron.add(...) / .update(...) persists anything; an attacker or agent cannot bypass it by crafting prompt text.
  • No trust-model change. Gateway auth scopes, CODEOWNERS-protected paths (auth, SSRF, sandbox, MCP, TLS), and channel plugin internals are not touched.
  • No secret handling change. No credential reads, env reads, or filesystem reads are added; only cfg.channels[<id>].accounts key enumeration.
  • No new error leakage. The error lists only configured account ids, which are already surfaced by openclaw doctor and channel status endpoints.
  • Failure-destination symmetry keeps the delivery.failureDestination path from becoming a silent bypass around the same class of misconfiguration.

Tests

Added two focused tests in src/gateway/server.cron.test.ts:

  1. rejects cron.add when delivery.accountId is not a configured channel account — configures channels.telegram.accounts.{sentry,other}, sends cron.add with accountId: "nonexistent_bot_xyz", asserts the rejection references delivery.accountId and lists the configured ids.
  2. accepts cron.add when delivery.accountId is a configured channel account — same shape but with accountId: "sentry", asserts ok: true.

Test plan

Commands run locally from this branch:

  • pnpm test src/gateway/server.cron.test.ts -t "accountId" → 2 passed, 16 skipped

  • pnpm test src/gateway/server.cron.test.ts → 18 passed (full file, including the two new cases and existing announce-delivery rejection tests)

  • pnpm check:changed → typecheck core + core tests, oxlint core (0 warnings / 0 errors), import cycles, webhook body / pairing guards, and gateway test shard (466 passed across 40 files) all green

  • Changelog entry added under ## Unreleased### Fixes referencing #69513.

  • Fully tested (unit + scoped integration).

  • American English spelling; single-purpose PR; focused diff.

Made with Cursor

Changed files

  • CHANGELOG.md (modified, +1/-0)
  • src/gateway/server-methods/cron.ts (modified, +52/-0)
  • src/gateway/server.cron.test.ts (modified, +185/-0)

PR #2: fix(cron): validate announce account ids

Description (problem / solution / changelog)

<!-- CURSOR_AGENT_PR_BODY_BEGIN -->

Summary

  • Problem: cron.add and cron.update validated announce channels but still accepted explicit delivery.accountId values that do not exist for the chosen channel.
  • Why it matters: operators can create or patch cron jobs that look valid at write time but are guaranteed to fail or misroute at delivery time.
  • What changed: src/gateway/server-methods/cron.ts now validates explicit announce accountId values against configured accounts for the resolved channel, including failure destinations that use announce mode.
  • What did NOT change (scope boundary): delivery execution, account auto-resolution, and cron transport behavior were not changed; this is creation/update-time validation only.
  • AI-assisted: Yes. I implemented the fix and ran the scoped verification locally in this workspace.

Change Type (select all)

  • Bug fix
  • Feature
  • Refactor required for the fix
  • Docs
  • Security hardening
  • Chore/infra

Scope (select all touched areas)

  • Gateway / orchestration
  • Skills / tool execution
  • Auth / tokens
  • Memory / storage
  • Integrations
  • API / contracts
  • UI / DX
  • CI/CD / infra

Linked Issue/PR

  • Closes #69513
  • Related #69482
  • This PR fixes a bug or regression

Root Cause (if applicable)

  • Root cause: cron request validation checked that announce delivery channels were configured, but it never checked whether an explicitly provided account id belonged to that configured channel.
  • Missing detection / guardrail: gateway RPC tests covered channel validation but not invalid explicit announce account ids on add/update.
  • Contributing context (if known): multi-account channels make this more likely because a typo still looks syntactically valid and gets persisted unchanged.

Regression Test Plan (if applicable)

  • Coverage level that should have caught this:
    • Unit test
    • Seam / integration test
    • End-to-end test
    • Existing coverage already sufficient
  • Target test or file: src/gateway/server.cron.test.ts
  • Scenario the test should lock in: cron.add and cron.update reject announce deliveries whose explicit accountId is not configured for the selected channel.
  • Why this is the smallest reliable guardrail: the bug lives in gateway request validation, so the RPC test file exercises the real user-facing contract without needing full cron execution.
  • Existing test that already covers this (if any): none.
  • If no new test is added, why not: N/A.

User-visible / Behavior Changes

  • openclaw cron create and equivalent gateway cron.add / cron.update calls now reject invalid explicit announce account ids instead of storing a broken delivery target.

Diagram (if applicable)

Before:
[cron add/update with announce channel + bad accountId] -> [stored successfully] -> [later delivery failure/misroute]

After:
[cron add/update with announce channel + bad accountId] -> [gateway validation rejects request] -> [operator fixes accountId before save]

Security Impact (required)

  • New permissions/capabilities? (Yes/No) No
  • Secrets/tokens handling changed? (Yes/No) No
  • New/changed network calls? (Yes/No) No
  • Command/tool execution surface changed? (Yes/No) No
  • Data access scope changed? (Yes/No) No
  • If any Yes, explain risk + mitigation:

Repro + Verification

Environment

  • OS: Ubuntu Linux in Cursor Cloud
  • Runtime/container: Node 22 + pnpm workspace
  • Model/provider: N/A
  • Integration/channel (if any): Gateway cron RPC validation
  • Relevant config (redacted): test config with Telegram announce channel enabled

Steps

  1. Configure a channel with one valid account.
  2. Call cron.add or cron.update with delivery.mode="announce", that channel, and a nonexistent accountId.
  3. Observe gateway response.

Expected

  • The gateway rejects the request before persisting the job.

Actual

  • Before this fix, the gateway accepted and stored the invalid account id unchanged.

Evidence

  • Failing test/log before + passing after
  • Trace/log snippets
  • Screenshot/recording
  • Perf numbers (if relevant)

Human Verification (required)

  • Verified scenarios: ran pnpm test src/gateway/server.cron.test.ts; ran pnpm check:changed.
  • Edge cases checked: add and update both reject invalid explicit announce account ids; existing announce channel validation remains intact.
  • What you did not verify: live cron execution against a real multi-account channel deployment.

Review Conversations

  • I replied to or resolved every bot review conversation I addressed in this PR.
  • I left unresolved only the conversations that still need reviewer or maintainer judgment.

Compatibility / Migration

  • Backward compatible? (Yes/No) Yes
  • Config/env changes? (Yes/No) No
  • Migration needed? (Yes/No) No
  • If yes, exact upgrade steps:

Risks and Mitigations

  • Risk: configs that rely on implicit fallback behavior for malformed explicit account ids will now fail fast at write time.
    • Mitigation: validation only applies when an explicit non-empty accountId is provided; existing implicit/default-account flows are unchanged.
<!-- CURSOR_AGENT_PR_BODY_END --> <div><a href="https://cursor.com/agents/bc-a225a134-d59a-46cf-a2c4-14c5aaa6297d"><picture><source media="(prefers-color-scheme: dark)" srcset="https://cursor.com/assets/images/open-in-web-dark.png"><source media="(prefers-color-scheme: light)" srcset="https://cursor.com/assets/images/open-in-web-light.png"><img alt="Open in Web" width="114" height="28" src="https://cursor.com/assets/images/open-in-web-dark.png"></picture></a>&nbsp;<a href="https://cursor.com/background-agent?bcId=bc-a225a134-d59a-46cf-a2c4-14c5aaa6297d"><picture><source media="(prefers-color-scheme: dark)" srcset="https://cursor.com/assets/images/open-in-cursor-dark.png"><source media="(prefers-color-scheme: light)" srcset="https://cursor.com/assets/images/open-in-cursor-light.png"><img alt="Open in Cursor" width="131" height="28" src="https://cursor.com/assets/images/open-in-cursor-dark.png"></picture></a>&nbsp;</div>

Changed files

  • src/gateway/server-methods/cron.ts (modified, +78/-8)
  • src/gateway/server.cron.test.ts (modified, +371/-0)

Code Example

Step 1: openclaw cron create --account sentry ...
Created: b60070f5-1d7e-4d25-9c8d-c15465d411f7 (valid account, baseline)

Step 2: openclaw cron create --account nonexistent_bot_xyz ...
Created: f53c93c9-3e43-45b5-8c1d-889297c30a32 (invalid account, accepted)
Stored delivery.accountId: "nonexistent_bot_xyz" (unchanged, not validated)

---

# 1. Create a cron with a valid accountId (baseline)
openclaw cron create \
  --name "test-valid" \
  --agent sentry \
  --at "2027-01-01T00:00:00-06:00" \
  --announce \
  --channel telegram \
  --account sentry \
  --to 6522813629 \
  --message "test"
# Result: created successfully

# 2. Create a cron with an invalid accountId
openclaw cron create \
  --name "test-invalid" \
  --agent sentry \
  --at "2027-01-01T00:00:00-06:00" \
  --announce \
  --channel telegram \
  --account nonexistent_bot_xyz \
  --to 6522813629 \
  --message "test"
# Result: created successfully — should have been rejected

# 3. Verify stored value
# In cron/jobs.json, the invalid job has:
#   delivery.accountId: "nonexistent_bot_xyz"

# 4. Clean up
openclaw cron delete <valid-job-id>
openclaw cron delete <invalid-job-id>
RAW_BUFFERClick to expand / collapse

Product / Version

OpenClaw 2026.4.15 (041266a) — stable channel

Summary

openclaw cron create --announce --account <id> does not validate the accountId against configured channel accounts. A cron job created with a nonexistent account name is silently accepted and stored with the bogus value. At delivery time, this would cause a silent failure or misrouted delivery.

Expected Behavior

When creating a cron job with --announce --channel telegram --account nonexistent_bot_xyz, OpenClaw should either:

  • Reject the job with an error: "Account 'nonexistent_bot_xyz' is not a configured Telegram account"
  • Warn and refuse to persist the job
  • At minimum, log a warning at creation time

Actual Behavior

The job is created successfully. The invalid accountId is stored unchanged in cron/jobs.json:

Step 1: openclaw cron create --account sentry ...
  → Created: b60070f5-1d7e-4d25-9c8d-c15465d411f7 (valid account, baseline)

Step 2: openclaw cron create --account nonexistent_bot_xyz ...
  → Created: f53c93c9-3e43-45b5-8c1d-889297c30a32 (invalid account, accepted)
  → Stored delivery.accountId: "nonexistent_bot_xyz" (unchanged, not validated)

No error, no warning, no rejection. Both jobs were cleaned up after the test.

Impact

  • Silent delivery failure: A cron job targeting a nonexistent account will fail at delivery time with no feedback at creation time. The operator does not learn about the misconfiguration until the reminder is missed.
  • Misrouted reminders: In a multi-account setup, a typo in --account could route a reminder to the wrong channel account or silently drop it.
  • Audit gap: Operators reviewing cron/jobs.json cannot tell whether an accountId is valid without cross-referencing the channel config.

This is a LOW-severity bug because:

  • Most operators create cron jobs with known account names
  • The bug only manifests at delivery time, not creation time
  • Existing jobs with correct accountIds are not affected
  • There is no data loss — only missed or misrouted deliveries

Reproduction

# 1. Create a cron with a valid accountId (baseline)
openclaw cron create \
  --name "test-valid" \
  --agent sentry \
  --at "2027-01-01T00:00:00-06:00" \
  --announce \
  --channel telegram \
  --account sentry \
  --to 6522813629 \
  --message "test"
# Result: created successfully

# 2. Create a cron with an invalid accountId
openclaw cron create \
  --name "test-invalid" \
  --agent sentry \
  --at "2027-01-01T00:00:00-06:00" \
  --announce \
  --channel telegram \
  --account nonexistent_bot_xyz \
  --to 6522813629 \
  --message "test"
# Result: created successfully — should have been rejected

# 3. Verify stored value
# In cron/jobs.json, the invalid job has:
#   delivery.accountId: "nonexistent_bot_xyz"

# 4. Clean up
openclaw cron delete <valid-job-id>
openclaw cron delete <invalid-job-id>

Current Workaround

Manual verification: operators must cross-reference --account values against configured channel accounts before creating cron jobs.

Why Not Fixed in 2026.4.15

The 2026.4.15 changelog includes cron announce delivery fixes (#65004, #65016) for NO_REPLY suppression and mixed-content handling, but these address delivery-time behavior, not creation-time validation. No accountId validation was added.

Recommended Fix Direction

At cron create time, resolve the --account value against the configured accounts for the specified --channel:

  • If --channel telegram and --account X, check that channels.telegram.accounts.X exists
  • If not found, return an error and do not create the job
  • Optionally allow a --force flag to bypass validation for edge cases

Recommended Priority

LOW — cosmetic/preventive. No data loss. Affects operator UX and reduces trust in cron delivery reliability.

Note

This draft reflects the final observed live result from a controlled retest on 2026-04-20. Earlier retest attempts had CLI flag issues (--payload--message, date format) that produced stale/inaccurate output. The result documented here is from the successful final run with correct flags.


Draft prepared for GitHub filing. No real secrets included.

extent analysis

TL;DR

Validate the --account value against configured channel accounts at cron create time to prevent silent delivery failures and misrouted reminders.

Guidance

  • Modify the openclaw cron create command to check if the provided --account value exists in the configured accounts for the specified --channel.
  • If the account is not found, return an error message and do not create the job.
  • Consider adding a --force flag to bypass validation for edge cases.
  • Review the cron/jobs.json file to ensure that only valid account IDs are stored.

Example

# Example of modified openclaw cron create command with validation
openclaw cron create \
  --name "test-valid" \
  --agent sentry \
  --at "2027-01-01T00:00:00-06:00" \
  --announce \
  --channel telegram \
  --account sentry \
  --to 6522813629 \
  --message "test"
# If account is not found, return an error message
# Error: Account 'nonexistent_bot_xyz' is not a configured Telegram account

Notes

  • The current workaround requires manual verification of account IDs, which can be error-prone and time-consuming.
  • The recommended fix direction is to add validation at creation time to prevent silent delivery failures and misrouted reminders.

Recommendation

Apply the workaround by manually verifying account IDs until the fix is implemented, as the issue is classified as LOW-severity and does not cause data loss.

Vote matrix · Quick signals

Works
Did the solution work? Tap to confirm.
Easy Fix
Was it a quick fix?
Time Saver
Did it save you time?
Blocking
Was it severely blocking?
Common Issue
Are others likely hitting this too?
Flaky / Intermittent
Is it intermittent?
Verified / Reproducible
Can you reproduce it reliably?
Loading…

Still need to ship something?

×6

Another batch ranked right after the header list — different links, same matching logic.

Back to top recommendations

TRENDING

openclaw - ✅(Solved) Fix [Bug]: cron create silently accepts invalid delivery accountId without validation [2 pull requests]