Skip to content

fix: superseded runtime publication falls through to a quota error - #157218

Merged
obviyus merged 1 commit into
openclaw:mainfrom
gokay-ai:cursor/runtime-publication-superseded-no-fallback-742f
Sep 24, 2026
Merged

obviyus merged 1 commit into
openclaw:mainfrom
gokay-ai:cursor/runtime-publication-superseded-no-fallback-742f

Conversation

@gokay-ai

@gokay-ai gokay-ai commented Sep 24, 2026 •

Copy link
Copy Markdown
Contributor

Thanks for the clear write-up on #156975.

What Problem This Solves

Fixes: a superseded prepared-model-runtime publication is treated as an unknown provider failure, so fallback rotates to the next candidate and an exhausted OAuth fallback can surface as a quota or rate-limit warning.

Fixes #156975

User Impact

User impact: that internal ownership miss stays internal. OpenClaw does not rotate to an unrelated provider, and operators are not sent looking at OpenAI quota.

Why This Change Was Made

Failover now classifies prepared-owner not-published and publication-superseded errors as coordination. The existing candidate runner already stops rotation for that kind.

Evidence

Focused Vitest (node scripts/run-vitest.mjs <file> --maxWorkers=1):

  • src/agents/failover-error.test.ts: 12.3s wall (83 tests). Direct and wrapped owner-not-published / publication-superseded errors classify as coordination, not billing/rate-limit.
  • src/agents/model-fallback.terminal-boundary.test.ts: 17.0s wall (96 tests). Forcing the typed error on the primary candidate does not rotate to xai/grok-4.7 and does not surface the exhausted-fallback billing path.
  • git diff --check: clean.

Additional runtime evidence

Verified on 964bb1eba653f2188970d353b99cfeb4ff3544fb:

  • Real publication-owner race (3.06s wall): called publishModelRuntimeSnapshot twice without awaiting between calls, with the same agent/workspace identity and shared owner/build-completion maps, from the primary callback of the real runWithModelFallback. Both calls executed actual preparation in static catalog mode with plugins disabled. The second publication replaced the first registered owner; the first rejected from prepared-model-runtime.owner.ts:704 with PreparedModelRuntimePublicationSupersededError. The runner attempted only proof-primary, returned that unchanged internal error (no quota/billing text), and never invoked proof-fallback. The second publication completed and was the sole current owner, with its snapshot published, no pending gate, and needsRefresh=false. No mocked publication, injected error, classifier replacement, or production modification was used in this proof.
  • Real Gateway negative control: an isolated built Gateway, launched using pnpm openclaw gateway, called a scripted loopback OpenAI-compatible primary returning HTTP 429. After the existing same-model retry policy, it made one request to the separately configured fallback endpoint and returned FALLBACK_OK through /v1/chat/completions. This protects real provider failover, not only classifier behavior.
  • Gateway reproduction limit: changing agents.defaults.model.fallbacks during that in-flight turn produced config hot reload applied, but did not produce publication supersession. Delaying model HTTP responses does not hold the static runtime preparation boundary: the model call happens after admission. Current Gateway admission joins replacement publication and retries a superseded acquisition when owner/generation facts advance (prepared-model-runtime-lease.ts:114-145,363-378); the publication queue also joins newer refreshes when requested. Thus an ordinary concurrent reload is not a deterministic trigger for the escaping error. The Windows OAuth production race has not been recreated; the deterministic proof above exercises the real lower-level publication owner and the changed fallback boundary instead. No claim that this patch repairs the underlying publication race.

The intended behavior at this boundary is to preserve the internal error, not rotate providers or invent an arbitrary retry. Reacquisition remains owned by the existing lease/publication lifecycle.

Matched real-owner before/after

Ran the same portable owner-race script against a full source snapshot of pinned origin/main (9e007615ece5436bc5d88d0d21ba0c4c62b408ec, its own frozen-lockfile dependencies) and the contributor head (964bb1eba653f2188970d353b99cfeb4ff3544fb). No module/classifier substitution was used. The fallback callback makes a real HTTP request to a loopback provider returning a synthetic HTTP 402 billing response.

Observation Pinned main (11.00s wall) Candidate (5.29s wall)
Primary preparation Real owner throws PreparedModelRuntimePublicationSupersededError Same
Candidates attempted proof-primary, proof-fallback proof-primary only
Fallback HTTP request One; status 402 None
Returned failure FailoverError: All models failed (2), supersession (unknown) followed by synthetic exhausted balance (billing) Original PreparedModelRuntimePublicationSupersededError, no billing/quota advice
Replacement publication Published, sole current owner, no pending gate Same

The separately exercised Gateway HTTP 429 control above still returned FALLBACK_OK. These results distinguish internal publication coordination from actual provider failures.

Observed owner-race output (paths redacted)
[
  {
    "revision": "9e007615ece5436bc5d88d0d21ba0c4c62b408ec",
    "variant": "baseline",
    "attempted": [
      "proof-primary",
      "proof-fallback"
    ],
    "primaryErrorName": "PreparedModelRuntimePublicationSupersededError",
    "errorName": "FailoverError",
    "message": "All models failed (2): proof-primary/model: prepared model runtime publication was superseded for <isolated-agent-dir> (unknown) | proof-fallback/model: {\"error\":{\"message\":\"synthetic usage balance exhausted\",\"type\":\"billing_error\"}} (billing)",
    "providerStatus": 402,
    "replacementPublished": true,
    "replacementCurrent": true
  },
  {
    "revision": "964bb1eba653f2188970d353b99cfeb4ff3544fb",
    "variant": "candidate",
    "attempted": [
      "proof-primary"
    ],
    "primaryErrorName": "PreparedModelRuntimePublicationSupersededError",
    "errorName": "PreparedModelRuntimePublicationSupersededError",
    "message": "prepared model runtime publication was superseded for <isolated-agent-dir>",
    "replacementPublished": true,
    "replacementCurrent": true
  }
]

Classify prepared-owner not-published and publication-superseded errors as
coordination so fallback cannot rotate to an unrelated provider or surface
an exhausted OAuth candidate as a quota warning.

Fixes openclaw#156975

Signed-off-by: GokayAI <[email protected]>
Copilot AI lite review requested due to automatic review settings September 24, 2026 10:42

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@clawsweeper

clawsweeper Bot commented Sep 24, 2026 •

Copy link
Copy Markdown
Contributor

🦞👀
ClawSweeper picked this up.

Pull request received. I will update this pull request when review starts.

ClawSweeper review complete

ClawSweeper finished reviewing this revision. The review result is being finalized.

View the workflow run.

@openclaw-barnacle openclaw-barnacle Bot added agents Agent runtime and tooling size: S labels Sep 24, 2026
@clawsweeper clawsweeper Bot added P1 High-priority user-facing bug, regression, or broken workflow. rating: 🦪 silver shellfish Thin PR readiness signal; proof, validation, or implementation needs work. status: 📣 needs proof The PR needs real behavior proof before ClawSweeper can clear the contributor ask. labels Sep 24, 2026
@clawsweeper

clawsweeper Bot commented Sep 24, 2026 •

Copy link
Copy Markdown
Contributor

Codex review: needs maintainer review before merge. Reviewed September 24, 2026, 9:34 AM ET / 13:34 UTC (Revision 2).

ClawSweeper review

What this changes

The branch makes OpenClaw stop model fallback when preparation reports an unpublished or superseded runtime owner, and adds classification and fallback tests.

Regression provenance

Possible regression — probable (reviewed change; failure trace). No predecessor PR is attributed.

Merge readiness

✅ Ready for maintainer review

The classification gap remains on current main. The new before-and-after publication-owner trace addresses the prior proof request at the changed fallback boundary, and the separate Gateway control shows ordinary provider fallback still works. A second open PR addresses the same issue, but neither has merged.

Priority: P1
Reviewed head: 964bb1eba653f2188970d353b99cfeb4ff3544fb

Review scores

Measure Result What it means
Overall readiness 🐚 platinum hermit (4/6) A focused classifier repair has strong boundary-level before-and-after proof and no discrete correctness finding.
Proof confidence 🦞 diamond lobster (5/6) Sufficient (terminal): The branch changes OpenClaw's fallback classifier. The captured before-and-after trace drives an actual publication-owner supersession through the real fallback runner, observes that the branch makes no fallback HTTP request, and shows ordinary HTTP 429 Gateway fallback still succeeds. It does not establish the original Windows OAuth race; no stored-data contract changes.
Patch quality 🐚 platinum hermit (4/6) No actionable review findings were identified.

Verification

Check Result Evidence
Real behavior Verified Sufficient (terminal): The branch changes OpenClaw's fallback classifier. The captured before-and-after trace drives an actual publication-owner supersession through the real fallback runner, observes that the branch makes no fallback HTTP request, and shows ordinary HTTP 429 Gateway fallback still succeeds. It does not establish the original Windows OAuth race; no stored-data contract changes.
Evidence reviewed 7 items Introduced classification: The pinned branch adds a class-based check for unpublished runtime owners before provider failure coercion.
Fallback boundary: The existing candidate runner throws coordination errors before recording a failed candidate or advancing to another model.
Current main remains affected: Current main lacks the branch's prepared-owner classification check, so it has not independently solved this PR's central behavior.
Findings None None.
Security None None.

How this fits together

The agent runner prepares a runtime before trying a selected model and its configured fallback models. Its error classifier decides whether a failure belongs to a provider or to OpenClaw's own runtime coordination, which determines whether the next provider is called.

flowchart LR
  A[Agent turn] --> B[Prepared runtime]
  B --> C{Failure type}
  C -->|Provider failure| D[Fallback model]
  C -->|Runtime coordination| E[Preserve internal error]
  D --> F[Provider response]
Loading

Before merge

None.

Agent review details

Security

None.

PR surface

Source +12, Tests +82. Total +94 across 3 files.

View PR surface stats
Area Files Added Removed Net
Source 1 12 0 +12
Tests 2 82 0 +82
Docs 0 0 0 0
Config 0 0 0 0
Generated 0 0 0 0
Other 0 0 0 0
Total 3 94 0 +94

Review metrics

None.

Root-cause cluster

Relationship: fixed_by_candidate
Canonical: #156975
Summary: Two open PRs propose overlapping fixes for the same reported fallback misclassification; neither has merged.

Members:

Proposal only: this assessment does not dispatch repair, suppress jobs, mutate sibling items, close, or merge anything.

Technical review

Best possible solution:

Keep runtime-owner reacquisition in the lease lifecycle and let the fallback classifier preserve any ownership error that escapes it; resolve the two open fix PRs through normal review without combining their scopes.

Do we have a high-confidence way to reproduce the issue?

Yes, for the changed boundary: the captured before-and-after race invokes the real publication owner from the fallback runner and records the different provider requests and returned errors. The original Windows OAuth Gateway sequence remains nondeterministic.

Is this the best way to solve the issue?

Yes. Classifying this typed internal ownership error where fallback decisions are made is a narrow repair; the existing lease owner remains responsible for reacquisition.

AGENTS.md: found and applied where relevant.

Codex review notes: model internal, reasoning medium; reviewed against 3948a0fd1831.

Labels

Label changes:

  • add proof: sufficient: Contributor real behavior proof is sufficient. The branch changes OpenClaw's fallback classifier. The captured before-and-after trace drives an actual publication-owner supersession through the real fallback runner, observes that the branch makes no fallback HTTP request, and shows ordinary HTTP 429 Gateway fallback still succeeds. It does not establish the original Windows OAuth race; no stored-data contract changes.
  • add rating: 🐚 platinum hermit: Overall readiness is 🐚 platinum hermit; proof is 🦞 diamond lobster and patch quality is 🐚 platinum hermit.
  • add status: 👀 ready for maintainer look: ClawSweeper has no concrete contributor-facing blocker left for this PR. Sufficient (terminal): The branch changes OpenClaw's fallback classifier. The captured before-and-after trace drives an actual publication-owner supersession through the real fallback runner, observes that the branch makes no fallback HTTP request, and shows ordinary HTTP 429 Gateway fallback still succeeds. It does not establish the original Windows OAuth race; no stored-data contract changes.
  • remove rating: 🦪 silver shellfish: Current PR rating is rating: 🐚 platinum hermit, so this older rating label is no longer current.
  • remove status: 📣 needs proof: Current PR status label is status: 👀 ready for maintainer look.

Label justifications:

  • P1: The reported agent turn lost its reply after an internal publication failure incorrectly advanced to a quota-exhausted fallback.
  • rating: 🐚 platinum hermit: Overall readiness is 🐚 platinum hermit; proof is 🦞 diamond lobster and patch quality is 🐚 platinum hermit.
  • status: 👀 ready for maintainer look: ClawSweeper has no concrete contributor-facing blocker left for this PR. Sufficient (terminal): The branch changes OpenClaw's fallback classifier. The captured before-and-after trace drives an actual publication-owner supersession through the real fallback runner, observes that the branch makes no fallback HTTP request, and shows ordinary HTTP 429 Gateway fallback still succeeds. It does not establish the original Windows OAuth race; no stored-data contract changes.
  • proof: sufficient: Contributor real behavior proof is sufficient. The branch changes OpenClaw's fallback classifier. The captured before-and-after trace drives an actual publication-owner supersession through the real fallback runner, observes that the branch makes no fallback HTTP request, and shows ordinary HTTP 429 Gateway fallback still succeeds. It does not establish the original Windows OAuth race; no stored-data contract changes.

Evidence

What I checked:

Likely related people:

  • Peter Steinberger: Suggested for follow-up; no historical authorship or introduction is verified. (role: unverified routing candidate; confidence: low)
  • Mert Başar: Suggested for follow-up; no historical authorship or introduction is verified. (role: unverified routing candidate; confidence: low)

Rating scale

Score Internal tier Crab rank Meaning
6/6 S 🦀 challenger crab Exceptional readiness
5/6 A 🦞 diamond lobster Very strong readiness
4/6 B 🐚 platinum hermit Good normal PR; ordinary maintainer review
3/6 C 🦐 gold shrimp Useful, but confidence is limited
2/6 D 🦪 silver shellfish Proof or implementation needs work
1/6 F 🧂 unranked krab Not merge-ready
N/A NA 🌊 off-meta tidepool Rating does not apply

Overall follows the weaker of proof and patch quality.
Shiny media proof means a screenshot, video, or linked artifact directly shows the changed behavior. Runtime, network, CSP, and security claims still need visible diagnostics.

Workflow

  • ClawSweeper keeps one durable marker-backed review comment per issue or PR.
  • Re-runs edit this comment so the latest verdict, findings, and automation markers stay together instead of adding duplicate bot comments.
  • A fresh review can be triggered by eligible @clawsweeper re-review comments, exact-item GitHub events, scheduled/background review runs, or manual workflow dispatch.
  • PR/issue authors and users with repository write access can comment @clawsweeper re-review or @clawsweeper re-run on an open PR or issue to request a fresh review only.
  • Maintainers can also comment @clawsweeper review to request a fresh review only.
  • Fresh-review commands do not start repair, autofix, rebase, CI repair, or automerge.
  • Maintainer-only repair and merge flows require explicit commands such as @clawsweeper autofix, @clawsweeper automerge, @clawsweeper fix ci, or @clawsweeper address review.
  • Maintainers can comment @clawsweeper explain to ask for more context, or @clawsweeper stop to stop active automation.

History

Review history (1 earlier review cycle)
  • reviewed 2026-09-24T10:52:15.628Z sha 964bb1e :: needs real behavior proof before merge. :: none

@obviyus

obviyus commented Sep 24, 2026

Copy link
Copy Markdown
Collaborator

@clawsweeper re-review

Added real publication-owner race evidence and a real isolated Gateway HTTP-429 fallback control to the PR body. Two competing actual preparations produce the typed supersession inside the real publication owner; the real fallback runner preserves it without rotating, while the replacement publishes successfully. The Gateway config-reload experiment and its reproduction limit are stated explicitly; this is not claimed as an OAuth Gateway race reproduction.

@clawsweeper

clawsweeper Bot commented Sep 24, 2026 •

Copy link
Copy Markdown
Contributor

🦞👀
Exact review queued.

Re-review progress:

@clawsweeper clawsweeper Bot added proof: sufficient ClawSweeper judged the real behavior proof convincing. rating: 🐚 platinum hermit Good normal PR readiness with ordinary maintainer review expected. status: 👀 ready for maintainer look ClawSweeper has no concrete contributor-facing blocker left for this PR. and removed rating: 🦪 silver shellfish Thin PR readiness signal; proof, validation, or implementation needs work. status: 📣 needs proof The PR needs real behavior proof before ClawSweeper can clear the contributor ask. labels Sep 24, 2026
@obviyus
obviyus merged commit 23f89ee into openclaw:main Sep 24, 2026
224 of 226 checks passed
@obviyus

obviyus commented Sep 24, 2026

Copy link
Copy Markdown
Collaborator

Landed as 23f89ee. Thanks @gokay-ai for the fix and @WG-Mojo for the report.

Before: two real competing runtime publications on pinned main sent the superseded primary into fallback, made an HTTP 402 request, and returned an aggregate billing error. After: the same publication-owner race returned the original typed internal error, made no fallback request, and left the replacement runtime published/current. A separate isolated built Gateway control with primary HTTP 429 still reached its fallback and returned FALLBACK_OK. The original Windows OAuth Gateway race was not recreated; that limit is documented in the PR body. Exact-head CI was green and hosted ClawSweeper marked the evidence sufficient.

@roboclaw-bot roboclaw-bot mentioned this pull request Sep 24, 2026
2 of 8 tasks
github-actions Bot pushed a commit to Desicool/openclaw that referenced this pull request Sep 25, 2026
…openclaw#157218)

Thanks for the clear write-up on openclaw#156975.

## What Problem This Solves

Fixes: a superseded prepared-model-runtime publication is treated as an unknown provider failure, so fallback rotates to the next candidate and an exhausted OAuth fallback can surface as a quota or rate-limit warning.

Fixes openclaw#156975

## User Impact

User impact: that internal ownership miss stays internal. OpenClaw does not rotate to an unrelated provider, and operators are not sent looking at OpenAI quota.

## Why This Change Was Made

Failover now classifies prepared-owner not-published and publication-superseded errors as coordination. The existing candidate runner already stops rotation for that kind.

## Evidence

Focused Vitest (`node scripts/run-vitest.mjs <file> --maxWorkers=1`):

- `src/agents/failover-error.test.ts`: 12.3s wall (83 tests). Direct and wrapped owner-not-published / publication-superseded errors classify as coordination, not billing/rate-limit.
- `src/agents/model-fallback.terminal-boundary.test.ts`: 17.0s wall (96 tests). Forcing the typed error on the primary candidate does not rotate to `xai/grok-4.7` and does not surface the exhausted-fallback billing path.
- `git diff --check`: clean.

## Additional runtime evidence

Verified on `964bb1eba653f2188970d353b99cfeb4ff3544fb`:

- **Real publication-owner race (3.06s wall):** called `publishModelRuntimeSnapshot` twice without awaiting between calls, with the same agent/workspace identity and shared owner/build-completion maps, from the primary callback of the real `runWithModelFallback`. Both calls executed actual preparation in static catalog mode with plugins disabled. The second publication replaced the first registered owner; the first rejected from `prepared-model-runtime.owner.ts:704` with `PreparedModelRuntimePublicationSupersededError`. The runner attempted only `proof-primary`, returned that unchanged internal error (no quota/billing text), and never invoked `proof-fallback`. The second publication completed and was the sole current owner, with its snapshot published, no pending gate, and `needsRefresh=false`. No mocked publication, injected error, classifier replacement, or production modification was used in this proof.
- **Real Gateway negative control:** an isolated built Gateway, launched using `pnpm openclaw gateway`, called a scripted loopback OpenAI-compatible primary returning HTTP 429. After the existing same-model retry policy, it made one request to the separately configured fallback endpoint and returned `FALLBACK_OK` through `/v1/chat/completions`. This protects real provider failover, not only classifier behavior.
- **Gateway reproduction limit:** changing `agents.defaults.model.fallbacks` during that in-flight turn produced `config hot reload applied`, but did not produce publication supersession. Delaying model HTTP responses does not hold the static runtime preparation boundary: the model call happens after admission. Current Gateway admission joins replacement publication and retries a superseded acquisition when owner/generation facts advance (`prepared-model-runtime-lease.ts:114-145,363-378`); the publication queue also joins newer refreshes when requested. Thus an ordinary concurrent reload is not a deterministic trigger for the escaping error. The Windows OAuth production race has not been recreated; the deterministic proof above exercises the real lower-level publication owner and the changed fallback boundary instead. No claim that this patch repairs the underlying publication race.

The intended behavior at this boundary is to preserve the internal error, not rotate providers or invent an arbitrary retry. Reacquisition remains owned by the existing lease/publication lifecycle.

### Matched real-owner before/after

Ran the **same portable owner-race script** against a full source snapshot of pinned `origin/main` (`fd4d10858d5d2a187cf6f6d95d782234b4fde6d6`, its own frozen-lockfile dependencies) and the contributor head (`964bb1eba653f2188970d353b99cfeb4ff3544fb`). No module/classifier substitution was used. The fallback callback makes a real HTTP request to a loopback provider returning a synthetic HTTP 402 billing response.

| Observation | Pinned main (11.00s wall) | Candidate (5.29s wall) |
| --- | --- | --- |
| Primary preparation | Real owner throws `PreparedModelRuntimePublicationSupersededError` | Same |
| Candidates attempted | `proof-primary`, `proof-fallback` | `proof-primary` only |
| Fallback HTTP request | One; status 402 | None |
| Returned failure | `FailoverError`: `All models failed (2)`, supersession `(unknown)` followed by synthetic exhausted balance `(billing)` | Original `PreparedModelRuntimePublicationSupersededError`, no billing/quota advice |
| Replacement publication | Published, sole current owner, no pending gate | Same |

The separately exercised Gateway HTTP 429 control above still returned `FALLBACK_OK`. These results distinguish internal publication coordination from actual provider failures.

<details>
<summary>Observed owner-race output (paths redacted)</summary>

```json
[
  {
    "revision": "fd4d10858d5d2a187cf6f6d95d782234b4fde6d6",
    "variant": "baseline",
    "attempted": [
      "proof-primary",
      "proof-fallback"
    ],
    "primaryErrorName": "PreparedModelRuntimePublicationSupersededError",
    "errorName": "FailoverError",
    "message": "All models failed (2): proof-primary/model: prepared model runtime publication was superseded for <isolated-agent-dir> (unknown) | proof-fallback/model: {\"error\":{\"message\":\"synthetic usage balance exhausted\",\"type\":\"billing_error\"}} (billing)",
    "providerStatus": 402,
    "replacementPublished": true,
    "replacementCurrent": true
  },
  {
    "revision": "964bb1eba653f2188970d353b99cfeb4ff3544fb",
    "variant": "candidate",
    "attempted": [
      "proof-primary"
    ],
    "primaryErrorName": "PreparedModelRuntimePublicationSupersededError",
    "errorName": "PreparedModelRuntimePublicationSupersededError",
    "message": "prepared model runtime publication was superseded for <isolated-agent-dir>",
    "replacementPublished": true,
    "replacementCurrent": true
  }
]
```

</details>

Co-authored-by: Ayaan Zaidi <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

agents Agent runtime and tooling P1 High-priority user-facing bug, regression, or broken workflow. proof: sufficient ClawSweeper judged the real behavior proof convincing. rating: 🐚 platinum hermit Good normal PR readiness with ordinary maintainer review expected. size: S status: 👀 ready for maintainer look ClawSweeper has no concrete contributor-facing blocker left for this PR.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: Superseded model-runtime publication triggers provider fallback and misleading quota error on OAuth routes

3 participants