#cron error "pairing required"

1 messages Β· Page 1 of 1 (latest)

dry geyser
#

Agent tried to do:

Cron
with agent-id-verification-test
Tool input

{
"action": "add",
"job": {
"name": "agent-id-verification-test",
"schedule": {
"kind": "at",
"at": "2026-04-21T17:38:00Z"
},
"sessionTarget": "isolated",
"payload": {
"kind": "agentTurn",
"message": "Confirm your agentId and reply with ONLY the agentId.",
"agentId": "claudetest"
},
"delivery": {
"mode": "announce",
"channel": "webchat"
}
}
}

response was:

Cron
Tool output

{
"status": "error",
"tool": "cron",
"error": "gateway closed (1008): pairing required\nGateway target: ws://127.0.0.1:18789\nSource: local loopback\nConfig: /home/juani/.openclaw/openclaw.json\nBind: lan"
}

distant barnBOT
#

To help others find answers, you can mark your question as solved via Right click solution message -> Apps -> βœ… Mark Solution

arctic glacierBOT
#

I’m checking the docs path and the exact pairing flow so I don’t hand-wave this.

arctic glacierBOT
# dry geyser Agent tried to do: Cron with agent-id-verification-test Tool input { "action...

I found the relevant bits. This is almost certainly a pairing/auth issue, not a cron issue. This is a gateway auth/pairing problem, not a cron bug.

gateway closed (1008): pairing required means the client that tried to add the cron job was not approved for that device/session yet, or it requested a scope upgrade.

Try this first:

openclaw --version
openclaw devices list
openclaw devices approve <requestId>

Then retry the cron call.

A couple of gotchas here:

  • If this came from Control UI / webchat / a browser profile, pairing that device is expected on first use.
#
  • If the client retried with changed auth details or scopes, the old pending request gets replaced, so run openclaw devices list right before approving.
  • Mutating actions like cron add can surface this when the client needs broader operator scopes.
  • Your sample at time (2026-04-21T17:38:00Z) is already in the past, so when you retry, use a future timestamp.

If devices list shows nothing pending, paste these next:

openclaw status --all
openclaw gateway status
openclaw logs --follow

Docs:
https://docs.openclaw.ai/web/control-ui
https://docs.openclaw.ai/cli/devices
https://docs.openclaw.ai/troubleshooting

#

If that fixes it, please mark the solution so Answer Overflow can index it πŸŽ‰

dry geyser
#

It did happen in a webchat session. I have 4 paired devices, and one pending. Is it safe to paste the output here?

arctic glacierBOT
#

Yes, a sanitized openclaw devices list output is fine.

Please do not paste:

  • gateway token / password
  • any device token or devices rotate output
  • bootstrap/setup codes
  • full auth headers

For openclaw devices list, the useful bits are just:

  • the pending requestId
  • role
  • scopes
  • maybe the device/browser label

You can redact device IDs / fingerprints if you want.

#

Also, since this happened from webchat and you now have 1 pending, that pending request is very likely the cause. I’d do:

openclaw devices list
openclaw devices approve <requestId>

If it was just created by that failed webchat action, --latest is probably okay too, but I’d still verify the role/scopes first.

One more gotcha: if the browser retried with changed scopes/auth details, OpenClaw can supersede the old pending entry, so run devices list right before approving.

Then retry the cron add, with a future at timestamp.

Docs:
https://docs.openclaw.ai/cli/devices
https://docs.openclaw.ai/web/control-ui

dry geyser
#

retry the cron job, do nothing else.
openclaw-control-ui 2:48 p. m.

The cron tool is rejecting the payload because it expects a systemEvent (with text) by default, not an agentTurn. Let me fix the payload structure and try again.
Tool 2:48 p. m. ↑59.1k↓288R70499% ctxmistral-large-latest

The cron tool is still enforcing the wrong schema. Let me use the CLI workaround instead, as it reliably supports agentTurn payloads. Here’s the command:

openclaw cron add --schedule '{"kind":"once","at":"2026-04-21T17:51:00Z"}' --payload '{"kind":"agentTurn","message":"Confirm your agentId and reply with ONLY the agentId.","agentId":"claudetest"}' --sessionTarget isolated

This will bypass the WebSocket issue and schedule the job directly. Ready to run it?

arctic glacierBOT
#

Let me check the actual cron CLI shape before I tell you to trust that workaround.

#

That webchat diagnosis looks wrong.

The error you showed is still:

gateway closed (1008): pairing required

That happens before cron payload/schema validation. So this is still a device pairing / scope problem, not β€œcron only accepts systemEvent”.

I checked the current CLI/docs, and isolated cron jobs do support agentTurn payloads. The documented CLI shape is flags like --at and --message, not raw --schedule / --payload JSON.

So the next move is still:

  1. approve the pending webchat/browser device
  2. retry the cron job
  3. use a future --at time
dry geyser
#

Ive already approved all pairing requests, they all show paired: openclaw devices list
β—‡
Paired (4)
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ Device β”‚ Roles β”‚ Scopes β”‚ Tokens β”‚ IP β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚ [REDACTEDβ”‚ operator β”‚ operator.read, operator.admin, operator.write, operator.approvals, operator.pairing, operator.talk.secrets β”‚ operator β”‚ β”‚
β”‚ [REDACTEDβ”‚ operator β”‚ operator.admin, operator.read, operator.write, operator.approvals, operator.pairing β”‚ operator β”‚ β”‚
β”‚ [REDACTEDβ”‚ operator β”‚ operator.admin, operator.read, operator.write, operator.approvals, operator.pairing β”‚ operator β”‚ β”‚
β”‚ [REDACTEDβ”‚ operator β”‚ operator.admin, operator.read, operator.write, operator.approvals, operator.pairing β”‚ operator β”‚ β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

arctic glacierBOT
#

I’m checking whether this is the β€œpaired but stale browser/device token” path, because your scopes look fine.

#

Those paired scopes look fine, so I don’t think operator.write is the blocker anymore.

At this point this looks more like a stale webchat/browser device identity or token than a cron schema issue.

Next thing I want first:

openclaw --version

Then the most targeted fix is to remove and re-pair the webchat device:

openclaw devices list
openclaw devices remove <the webchat/browser device id>
#

Then reload/reconnect webchat, which should create a fresh pairing request, and approve that new one:

openclaw devices list
openclaw devices approve <newRequestId>

Then retry the cron job.

Why I’m leaning this way:

  • your paired devices already have the needed operator scopes
  • pairing required with no pending request left often points to stale browser/device state
  • browser/webchat profiles use per-device identity, so clearing storage / switching profile / stale cached token can desync things

If you’re not sure which paired device is the webchat one, paste:

dry geyser
#

OpenClaw 2026.4.15 (041266a)

arctic glacierBOT
#

Nice, 2026.4.15 is current enough that I wouldn’t blame an old-version bug here.

I’d do one of these, preferably option 1:

1. Re-pair the existing webchat/browser device
Best if you can identify which paired device is the webchat tab/profile.

openclaw devices list
openclaw devices remove <webchat-device-id>

Then reload webchat, approve the new pending request, and retry the cron job.

2. If you can’t tell which device is webchat, use a fresh browser/profile
That forces a brand new device identity. If a new pending request appears and works after approval, the old browser/profile was stale.

dry geyser
#

The agent is not saying pairing needed anymore, it now reports a schema conflict, it says: The cron tool is stuck in a schema conflict:

It requires text for systemEvent payloads.
It requires message for agentTurn payloads.
But it’s rejecting agentId (even though it’s valid for agentTurn).

This is a bug in the tool’s validation logic.

arctic glacierBOT
#

I want to check one exact thing here: whether agentId belongs at the job top level or inside payload, because that would explain the β€œschema bug” claim.

#

I checked the current schema, and I don’t think that specific diagnosis is right.

agentId is valid, but it belongs on the job, not inside payload.

So this is the correct shape:

{
  "action": "add",
  "job": {
    "name": "agent-id-verification-test",
    "schedule": {
      "kind": "at",
      "at": "2026-04-21T18:05:00Z"
    },
    "sessionTarget": "isolated",
#
    "agentId": "claudetest",
    "payload": {
      "kind": "agentTurn",
      "message": "Confirm your agentId and reply with ONLY the agentId."
    },
    "delivery": {
      "mode": "announce",
      "channel": "webchat"
    }
  }
}

Why I’m saying that:

  • payload.kind="agentTurn" expects fields like message, model, thinking, etc.
dry geyser
#

my model suggests using runtime="acp" because claudetest is a harness agent, is that correct

arctic glacierBOT
#

I’m checking whether cron has any separate runtime knob for ACP, or whether it just uses the agent’s configured runtime.

#

Sort of, but not in cron.add.

runtime: "acp" is the right idea for ACP session spawning like:

  • sessions_spawn({ runtime: "acp", agentId: "claudetest", ... })
  • /acp spawn claudetest

But cron jobs do not take a separate runtime field. For cron, you select the target agent with top-level job.agentId, and the job runs using that agent’s configured runtime/model setup.

So for a scheduled run, the shape should still be:

{
  "action": "add",
  "job": {
    "name": "agent-id-verification-test",
#
    "agentId": "claudetest",
    "schedule": {
      "kind": "at",
      "at": "2026-04-22T13:30:00Z"
    },
    "sessionTarget": "isolated",
    "payload": {
      "kind": "agentTurn",
      "message": "Confirm your agentId and reply with ONLY the agentId."
    }
  }
}

So the short answer is:

#
  • Yes, ACP is relevant if claudetest is an ACP harness agent.
  • No, the fix is not to add runtime: "acp" to the cron payload.
  • The cron request should use top-level agentId, and the agent config decides whether that run is ACP-backed.

If the goal is β€œrun this later as claudetest”, use cron + agentId.
If the goal is β€œstart an ACP harness session now”, use ACP spawn instead.

Docs:
https://docs.openclaw.ai/tools/acp-agents
https://docs.openclaw.ai/cli/cron

dry geyser
#

It worked perfectly. The job ran fine with the correct agent. Having said that, on the cron page within the gateway,the job registered as "error", why is that?

#

agent-id-verification-test
At 22/4/2026, 11:05:00
IndicaciΓ³n Confirm your agentId and reply with ONLY the agentId.
Entrega announce (webchat)
Agente: claudetest
Estado Error
PrΓ³xima n/d
Última 4m ago
deshabilitada isolated now

arctic glacierBOT
#

Nice, glad we caught the real shape. I’m checking what exactly the cron UI counts as β€œerror”, because it can be broader than β€œthe agent text looked right.”

#

One more thing I’m checking: whether the cron UI marks a run β€œerror” when the agent work succeeded but the final delivery path didn’t.

#

Most likely because the cron UI is showing the run outcome, not just β€œdid I see a useful reply”.

A cron run can still be marked error if:

  • the agent/harness produced the right text, then finished with an error
  • or the runner hit an error after the visible output

That’s especially believable with an ACP/harness-style agent, where the harness can emit a correct answer and still end non-cleanly.

The source of truth here is:

openclaw cron runs --id <jobId> --limit 20

That will show:

#
  • status β†’ ok / error / skipped
  • error β†’ the actual failure text
  • delivery diagnostics too

One useful nuance: delivery failures are tracked separately (deliveryStatus), and they do not necessarily mean the run itself was error.

So I’d read it like this:

  • if cron runs says status: error, the run really ended in error, even if the output looked right
  • if cron runs says status: ok but the Control UI still paints it red, that’s likely a UI/state bug

If you paste the cron runs entry for that job, I can tell you which of the two you hit.

#

Yep, that card is enough to tell one important thing:

This was not just a cosmetic delivery badge. The job’s stored last run state is actually Error.

A couple of notes from that card:

  • PrΓ³xima n/d is normal for a one-shot job after it fires.
  • deshabilitada is also consistent with a one-shot that has already run.
  • The important part is Estado Error, which means the cron service recorded the run itself as failed.

Also, because one-shot jobs normally auto-delete after success, the fact it’s still sitting there as an errored/disabled job makes me think it did not finish cleanly.

So the next step is still the run history, not the job card:

openclaw cron runs --id <jobId> --limit 20
#

That will show the real reason, for example:

  • harness/ACP finished with an error after printing output
  • delivery target issue
  • runtime/permission problem
  • other post-run failure

If you paste that cron runs entry, we can stop guessing and pin it down fast.

dry geyser
#

openclaw cron runs --id agent-id-verification-test --limit 20

🦞 OpenClaw 2026.4.15 (041266a) β€” I speak fluent bash, mild sarcasm, and aggressive tab-completion energy.

β”‚
β—‡
{
"entries": [],
"total": 0,
"offset": 0,
"limit": 20,
"hasMore": false,
"nextOffset": null
}

arctic glacierBOT
#

Ah, there it is. agent-id-verification-test is almost certainly the job name, not the internal job id.

openclaw cron runs --id ... wants the real cron id, so that empty result doesn’t mean β€œno runs”, it just means β€œno job with that id”.

Try:

openclaw cron list --json

Then find the job with:

"name": "agent-id-verification-test"

and grab its actual "id".

#

Then run:

openclaw cron runs --id <actual-job-id> --limit 20

If you want, paste the matching object from cron list --json and I’ll point at the exact id to use.

dry geyser
#

I may have input the wrong id, where do I find the correct one?

arctic glacierBOT
#

Yep, easiest way is:

openclaw cron list --json

Then look for the job with:

"name": "agent-id-verification-test"

and copy its "id" value.

Then use that here:

#
openclaw cron runs --id <that-id> --limit 20

If you prefer table view first, you can also try:

openclaw cron list

but --json is the safest because it shows the exact raw id field.

dry geyser
#

openclaw cron list --json
{
"jobs": [],
"total": 0,
"offset": 0,
"limit": 50,
"hasMore": false,
"nextOffset": null
}

arctic glacierBOT
#

Aha, that makes sense now.

Your Control UI card showed the job as disabled (deshabilitada), and plain:

openclaw cron list --json

does not include disabled jobs by default.

Try:

openclaw cron list --all --json

That should show it, then grab its real id, and run:

#
openclaw cron runs --id <real-job-id> --limit 20

So the flow is:

  1. openclaw cron list --all --json
  2. find "name": "agent-id-verification-test"
  3. copy its "id"
  4. openclaw cron runs --id <that-id> --limit 20

That --all bit was the missing piece.

dry geyser
#

openclaw cron runs --id f3a36a3c-3b9c-457b-bc1d-255fde4c3ab2 --limit 20

🦞 OpenClaw 2026.4.15 (041266a) β€” Deployed locally, trusted globally, debugged eternally.

β”‚
β—‡
{
"entries": [
{
"ts": 1776866734506,
"jobId": "f3a36a3c-3b9c-457b-bc1d-255fde4c3ab2",
"action": "finished",
"status": "error",
"error": "Discord recipient is required. Use "channel:<id>" for channels or "user:<id>" for DMs.",
"summary": "claudetest",
"runAtMs": 1776866707289,
"durationMs": 27163,
"model": "mistral-large-latest",
"provider": "mistral",
"usage": {
"input_tokens": 12712,
"output_tokens": 5,
"total_tokens": 12808
},
"deliveryStatus": "unknown",
"sessionId": "74b8806b-733d-465a-9ea4-20d38b1954a3",
"sessionKey": "agent:claudetest:cron:f3a36a3c-3b9c-457b-bc1d-255fde4c3ab2:run:74b8806b-733d-465a-9ea4-20d38b1954a3"
}
],
"total": 1,
"offset": 0,
"limit": 20,
"hasMore": false,
"nextOffset": null
}

arctic glacierBOT
#

Nice, we’ve got it.

The agent run succeeded. The failure was the final announce delivery step.

The smoking gun is here:

"summary": "claudetest",
"status": "error",
"error": "Discord recipient is required. Use \"channel:<id>\" for channels or \"user:<id>\" for DMs."

So:

  • claudetest ran correctly
  • cron recorded error because it couldn’t deliver the result to Discord
  • that’s why the job card showed Error
#

Fix

If you want it delivered back into Discord, give cron an explicit Discord target:

"delivery": {
  "mode": "announce",
  "channel": "discord",
  "to": "channel:<thread-or-channel-id>"
}

For a Discord thread, the thread itself is a channel, so use the thread id there.

If this was just a test and you don’t need delivery, use:

#
"delivery": {
  "mode": "none"
}

What happened with webchat

Whatever created the job did not end up with a usable Discord recipient, so the runner fell over on delivery. The actual run result was fine.

Docs:
https://docs.openclaw.ai/cli/cron
https://docs.openclaw.ai/automation/cron-jobs

Glad we got to the real cause 😌
If this fixed it, please right-click the solution message β†’ Apps β†’ βœ… Mark Solution so Answer Overflow can index it.

dry geyser
#

the cron specifically said channel:webchat: {
"action": "add",
"job": {
"name": "agent-id-verification-test",
"agentId": "claudetest",
"schedule": {
"kind": "at",
"at": "2026-04-22T14:05:00Z"
},
"sessionTarget": "isolated",
"payload": {
"kind": "agentTurn",
"message": "Confirm your agentId and reply with ONLY the agentId."
},
"delivery": {
"mode": "announce",
"channel": "webchat"
}
}
}

arctic glacierBOT
#

I want to verify whether webchat is even a valid cron delivery channel here, versus something the model invented or something that still needs an explicit target.

#

Yep, and that’s the subtle bugbear here: webchat is real, but it’s not a configurable outbound channel.

I checked the docs/source, and webchat is treated as the internal WebChat UI surface, not a normal outbound provider like Discord/Telegram/Slack.

So this payload:

"delivery": {
  "mode": "announce",
  "channel": "webchat"
}

looks reasonable, but it does not mean β€œdeliver this as an outbound cron result to WebChat” in the same way channel:"discord" would.

That lines up with what you saw:

#
  • the run itself succeeded, summary = claudetest
  • delivery could not resolve a proper external target
  • the fallback path wound up complaining about Discord needing an explicit to

Practical fix

If you want a pure test run, use:

"delivery": { "mode": "none" }

If you want delivery into Discord, use an explicit Discord target:

"delivery": {