Skip to content

fix(discord): add timeouts to PluralKit lookup requests#104121

Merged
steipete merged 7 commits into
openclaw:mainfrom
Alix-007:alix/discord-pluralkit-timeouts
Jul 12, 2026
Merged

fix(discord): add timeouts to PluralKit lookup requests#104121
steipete merged 7 commits into
openclaw:mainfrom
Alix-007:alix/discord-pluralkit-timeouts

Conversation

@Alix-007

@Alix-007 Alix-007 commented Jul 11, 2026

Copy link
Copy Markdown
Contributor

What Problem This Solves

Discord PluralKit lookups are optional inbound message enrichment, but the PluralKit fetch and response body read did not have a deadline. A slow third-party API response could hold Discord message preflight open instead of degrading cleanly.

Why This Change Was Made

Add a small PluralKit lookup timeout, preserve the caller abort signal from preflight, and keep the timer active until the API body has been read. Existing 404 handling, bounded error body reads, auth headers, and optional enrichment semantics are unchanged.

The latest head also replaces the Discord-local timeout/abort helper with the shared SDK buildTimeoutAbortSignal helper, so PluralKit uses the same timeout diagnostics, safe timer bounds, parent abort relay, and cleanup contract as the rest of the plugin SDK HTTP timeout paths.

User Impact

If PluralKit is slow or hangs, Discord inbound message handling can abort the optional lookup instead of waiting indefinitely, keeping the core message flow responsive.

Evidence

Exact head after ordinary branch update: ab842be7a31b8ff2f77cec586265c35bde20a265.

Code/merge update:

  • Merged current upstream/main into alix/discord-pluralkit-timeouts with a normal merge commit; no rebase or force-push.
  • Resolved the only merge conflict in extensions/discord/src/pluralkit.ts.
  • Removed the local createPluralKitLookupSignal helper.
  • PluralKit now calls buildTimeoutAbortSignal from openclaw/plugin-sdk/extension-shared with timeoutMs: params.timeoutMs ?? 10_000, operation: "discord.pluralkit.lookup", and the redaction-safe PluralKit URL.
  • Cleanup remains in finally, so the deadline covers the fetch, non-OK bounded error-body reads, and successful JSON body reads.

Focused validation run in this worktree:

node scripts/run-vitest.mjs extensions/discord/src/pluralkit.test.ts
-> 1 file passed, 5 tests passed

TMPDIR=/tmp/openclaw-vitest-$USER node scripts/run-vitest.mjs extensions/discord/src/monitor/message-handler.preflight.test.ts
-> 1 file passed, 51 tests passed

node_modules/.bin/oxfmt --check --threads=1 extensions/discord/src/pluralkit.ts extensions/discord/src/pluralkit.test.ts extensions/discord/src/monitor/message-handler.preflight-pluralkit.ts
-> passed

node_modules/.bin/oxlint --threads=1 extensions/discord/src/pluralkit.ts extensions/discord/src/pluralkit.test.ts extensions/discord/src/monitor/message-handler.preflight-pluralkit.ts
-> passed

git diff --check -- extensions/discord/src/pluralkit.ts extensions/discord/src/pluralkit.test.ts extensions/discord/src/monitor/message-handler.preflight-pluralkit.ts
-> clean

Controlled stalled-host runtime proof from the current code path:

  • Started a local HTTP server that accepts requests and intentionally stalls either before headers or mid-body.
  • Called production resolveDiscordPreflightPluralKitInfo() with PluralKit enabled.
  • The helper loaded the production PluralKit runtime through loadPluralKitRuntime().
  • fetchPluralKitMessageInfo() used globalThis.fetch, wrapped by resolveFetch(), and the new SDK buildTimeoutAbortSignal path.
  • globalThis.fetch was temporarily routed from the PluralKit URL to the local stalled server so the network request, abort signal, body read, timeout, catch, and preflight null degradation path all ran in-process without Discord credentials.

Transcript:

proof: controlled local stalled-host through Discord preflight PluralKit runtime
fetch: route api.pluralkit.me/v2/messages/header-stall -> http://127.0.0.1:39717/v2/messages/header-stall
server: accepted /v2/messages/header-stall
[fetch-timeout] fetch timeout after 10000ms (elapsed 10001ms) operation=discord.pluralkit.lookup url=https://api.pluralkit.me/v2/messages/header-stall
preflight: header-stall result=null elapsed=11728ms
fetch: route api.pluralkit.me/v2/messages/body-stall -> http://127.0.0.1:39717/v2/messages/body-stall
server: request closed /v2/messages/header-stall after 10087ms
server: accepted /v2/messages/body-stall
[fetch-timeout] fetch timeout after 10000ms (elapsed 10001ms) operation=discord.pluralkit.lookup url=https://api.pluralkit.me/v2/messages/body-stall
preflight: body-stall result=null elapsed=10025ms
server: request closed /v2/messages/body-stall after 10033ms

Known proof limits:

  • This is controlled stalled-host proof through the production Discord preflight PluralKit helper path, not a live Discord message delivery recording.
  • Remote Crabbox/Testbox validation could not be used in this environment because both command -v crabbox and ../crabbox/bin/crabbox --version failed; the focused local test/proof commands above are the fallback.
  • GitHub CI for exact head ab842be7a31b8ff2f77cec586265c35bde20a265 is still running at the time of this body update.

@openclaw-barnacle openclaw-barnacle Bot added channel: discord Channel integration: discord size: S labels Jul 11, 2026
@clawsweeper clawsweeper Bot added rating: 🧂 unranked krab Not merge-ready due to missing proof or serious correctness/safety concerns. status: 📣 needs proof The PR needs real behavior proof before ClawSweeper can clear the contributor ask. P2 Normal backlog priority with limited blast radius. labels Jul 11, 2026
@clawsweeper

clawsweeper Bot commented Jul 11, 2026

Copy link
Copy Markdown
Contributor

Codex review: needs maintainer review before merge. Reviewed July 11, 2026, 5:48 AM ET / 09:48 UTC.

Summary
This PR adds a shared 10-second deadline and parent-abort propagation to Discord PluralKit lookup fetches and response-body reads, with focused stalled-body regression coverage.

PR surface: Source +16, Tests +41. Total +57 across 3 files.

Reproducibility: yes. Current main has no PluralKit deadline, and the exact-head controlled stalled-host path provides a high-confidence reproduction method for both header and body stalls.

Review metrics: none identified.

Root-cause cluster
Relationship: canonical
Canonical: #104121
Summary: This PR is the focused current landing candidate for the remaining PluralKit duration-bound gap; earlier broad attempts closed unmerged and the merged response-size hardening addresses a different failure mode.

Members:

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

Merge readiness
Overall: 🐚 platinum hermit
Proof: 🦞 diamond lobster
Patch quality: 🐚 platinum hermit
Result: ready for maintainer review.

Overall follows the weaker of proof and patch quality, so missing proof can cap an otherwise strong patch.

Rank-up moves:

  • none.

Next step before merge

  • No automated repair remains; the clean exact head should proceed through ordinary maintainer merge handling.

Security
Cleared: No concrete security or supply-chain concern was found; the patch adds no dependencies, permissions, secret handling, downloads, package-resolution changes, or new execution sources.

Review details

Best possible solution:

Merge the focused shared-helper integration after ordinary maintainer handling; keep the timeout internal and fixed rather than adding a Discord-local helper or user-facing configuration knob.

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

Yes. Current main has no PluralKit deadline, and the exact-head controlled stalled-host path provides a high-confidence reproduction method for both header and body stalls.

Is this the best way to solve the issue?

Yes. Reusing the plugin SDK timeout primitive at the Discord-owned lookup boundary is the narrowest maintainable fix; it covers the complete response lifecycle, relays caller cancellation, and preserves optional null degradation.

AGENTS.md: found and applied where relevant.

Codex review notes: model internal, reasoning high; reviewed against 2e2366b6d394.

Label changes

Label justifications:

  • P2: A stalled optional PluralKit request can delay Discord inbound preflight for enabled users, but the blast radius is limited to one channel integration.
  • 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 (live_output): The exact-head evidence exercises the production Discord preflight PluralKit path against controlled header and body stalls and records timeout diagnostics, request closure, and null degradation after the fix.
  • proof: sufficient: Contributor real behavior proof is sufficient. The exact-head evidence exercises the production Discord preflight PluralKit path against controlled header and body stalls and records timeout diagnostics, request closure, and null degradation after the fix.
Evidence reviewed

PR surface:

Source +16, Tests +41. Total +57 across 3 files.

View PR surface stats
Area Files Added Removed Net
Source 2 28 12 +16
Tests 1 41 0 +41
Docs 0 0 0 0
Config 0 0 0 0
Generated 0 0 0 0
Other 0 0 0 0
Total 3 69 12 +57

What I checked:

Likely related people:

  • vincentkoc: Commit 85a61aa refreshed the current Discord PluralKit, preflight, shared timeout, and plugin SDK baseline surfaces immediately before this PR. (role: recent area contributor; confidence: high; commits: 85a61aac2331; files: extensions/discord/src/pluralkit.ts, extensions/discord/src/monitor/message-handler.preflight-pluralkit.ts, src/utils/fetch-timeout.ts)
  • steipete: Git shortlog attributes the largest prior share of central PluralKit and preflight path history to Peter Steinberger, making this a useful secondary routing signal despite the recent file-topology refresh. (role: feature history contributor; confidence: medium; files: extensions/discord/src/pluralkit.ts, extensions/discord/src/monitor/message-handler.preflight-pluralkit.ts)
  • mcaxtr: Git shortlog shows multiple historical contributions by Marcus Castro to the central Discord PluralKit path. (role: feature history contributor; confidence: medium; files: extensions/discord/src/pluralkit.ts)
What the crustacean ranks mean
  • 🦀 challenger crab: rare, exceptional readiness with strong proof, clean implementation, and convincing validation.
  • 🦞 diamond lobster: very strong readiness with only minor maintainer review expected.
  • 🐚 platinum hermit: good normal PR, likely mergeable with ordinary maintainer review.
  • 🦐 gold shrimp: useful signal, but proof or patch confidence is still limited.
  • 🦪 silver shellfish: thin signal; proof, validation, or implementation needs work.
  • 🧂 unranked krab: not merge-ready because proof is missing/unusable or there are serious correctness or safety concerns.
  • 🌊 off-meta tidepool: rating does not apply to this item.

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.

How this review workflow works
  • 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.
Review history (5 earlier review cycles)
  • reviewed 2026-07-11T03:40:27.050Z sha a0d97d4 :: needs real behavior proof before merge. :: none
  • reviewed 2026-07-11T04:48:59.790Z sha ae5ab9a :: needs real behavior proof before merge. :: [P1] Reuse the plugin SDK timeout primitive
  • reviewed 2026-07-11T07:19:39.546Z sha ae5ab9a :: needs real behavior proof before merge. :: [P1] Reuse the plugin SDK timeout primitive
  • reviewed 2026-07-11T09:22:23.041Z sha ab842be :: needs maintainer review before merge. :: none
  • reviewed 2026-07-11T09:30:48.635Z sha ab842be :: needs maintainer review before merge. :: none

@Alix-007
Alix-007 force-pushed the alix/discord-pluralkit-timeouts branch from a0d97d4 to ae5ab9a Compare July 11, 2026 04:33
@Alix-007

Copy link
Copy Markdown
Contributor Author

@clawsweeper re-review

Updated exact head to ae5ab9a5c05787b6e6407eaeb2f4b52cf6f616cd.

Changes/proof since the prior review:

  • Rebased onto current upstream/main.
  • Kept the PluralKit lookup deadline scoped to optional Discord preflight enrichment.
  • Preserved caller abort propagation from Discord preflight into fetchPluralKitMessageInfo.

Validation:

node scripts/run-vitest.mjs extensions/discord/src/pluralkit.test.ts
-> 1 file passed, 5 tests passed

TMPDIR=/tmp/openclaw-vitest-$USER node scripts/run-vitest.mjs extensions/discord/src/monitor/message-handler.preflight.test.ts
-> 1 file passed, 51 tests passed

git diff --check
-> clean

Redacted live/runtime proof from the current head:

live PluralKit API lookup for a numeric nonexistent message id
controlled slow lookup using the production API with caller AbortSignal propagation

Output:

live-pluralkit-404: result=null in 800ms
caller-abort: AbortError after 52ms

I do not have a credentialed live Discord message-preflight environment in this local run, so this proof covers the live PluralKit API path plus the runtime caller-abort behavior directly at the patched boundary.

@clawsweeper

clawsweeper Bot commented Jul 11, 2026

Copy link
Copy Markdown
Contributor

🦞🧹
ClawSweeper re-review requested.

I asked ClawSweeper to review this item again.
Action: item re-review queued (workflow sweep.yml, event repository_dispatch).
Result: the existing ClawSweeper review comment will be edited in place when the review finishes.

Re-review progress:

@clawsweeper clawsweeper Bot added rating: 🦪 silver shellfish Thin PR readiness signal; proof, validation, or implementation needs work. and removed rating: 🧂 unranked krab Not merge-ready due to missing proof or serious correctness/safety concerns. labels Jul 11, 2026
@Alix-007

Copy link
Copy Markdown
Contributor Author

@clawsweeper re-review

Latest head ab842be7a31b8ff2f77cec586265c35bde20a265 addresses the previous blocker by replacing the Discord-local timeout helper with SDK buildTimeoutAbortSignal, preserving the 10s default deadline through fetch and response-body reads, and updating the PR body with controlled stalled-host proof through the production Discord preflight PluralKit runtime.

@clawsweeper

clawsweeper Bot commented Jul 11, 2026

Copy link
Copy Markdown
Contributor

🦞🧹
ClawSweeper re-review requested.

I asked ClawSweeper to review this item again.
Action: item re-review queued (workflow sweep.yml, event repository_dispatch).
Result: the existing ClawSweeper review comment will be edited in place when the review finishes.

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 Jul 11, 2026
@steipete steipete self-assigned this Jul 11, 2026
@clawsweeper clawsweeper Bot added rating: 🦐 gold shrimp Decent PR readiness signal, but merge confidence is limited. status: ⏳ waiting on author ClawSweeper has contributor-facing work open and is waiting for author action. and removed 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. labels Jul 12, 2026
@steipete
steipete merged commit e7c3f7b into openclaw:main Jul 12, 2026
110 of 115 checks passed
@steipete

Copy link
Copy Markdown
Contributor

Merged via squash.

github-actions Bot pushed a commit to Desicool/openclaw that referenced this pull request Jul 12, 2026
)

* fix(discord): add timeouts to PluralKit lookup requests

* fix(discord): bound PluralKit preflight cancellation

* docs(changelog): note PluralKit lookup deadline

* chore: keep changelog release-owned

* test(discord): reject PluralKit aborts with errors

---------

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

Labels

channel: discord Channel integration: discord P2 Normal backlog priority with limited blast radius. proof: sufficient ClawSweeper judged the real behavior proof convincing. rating: 🦐 gold shrimp Decent PR readiness signal, but merge confidence is limited. size: S status: ⏳ waiting on author ClawSweeper has contributor-facing work open and is waiting for author action.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants