Skip to content

bug(social): the REST fallback ignores its own timeoutMs, so a silent Bot Social server hangs it forever #2633

Closed

Description

@Birfy

Written-by: Chief-audit

Found by the 17th bounded audit round (audit-1107-abort) on main 2e93b8c09174ddb6e843593c8e7b203348a7cb41 (2026-11-07T19:04:20Z; file blob bbdaa9f0f468c5c157a35979169db4717ff6103e equals the tree entry).

The defect

plugins/social/src/mcp-client.js — createSocialApi() builds rest(method, path, body) at :334. It receives the same timeoutMs the MCP client does (:330-331), but its fetch at :340 never passes a signal:

const response = await fetchImpl(`${base}${path}`, { method, headers, body })

There is no signal from :334 to :348. A Bot Social server that accepts the connection and never answers makes rest wait without end: no timeout error, no settlement. The five call sites are the productised fallback used when the MCP tool is missing (withRest / :401, :417, :421, :447, :488, :515 — listGrants, revokeGrant, updateBotProfile, grantBotToFriend, declineGrant), reached from social.js:2934 and friends with no outer deadline. The production construction at social.js:544 passes no timeoutMs either, so even the default 30 s never applies.

The same file already does it right twice: inviteInfo (:375-378) and createFaceRoomApi pass AbortSignal.timeout(timeoutMs), and socialFetch (:41-90) honours init.signal (req.destroy()).

Reproduction (local server that accepts and never answers)

[server] POST /mcp connected at +22ms (held open)
mcp callTool(whoami) -> {"state":"rejected","text":"Bot Social whoami: no answer in 0s"} after 198ms
[server] GET /v1/invites/code123 connected at +218ms (held open)
inviteInfo(code) -> {"state":"rejected","text":"The operation was aborted"} after 199ms
[server] GET /v1/bots/b1/grants connected at +416ms (held open)
rest GET /v1/bots/b1/grants -> {"state":"PENDING"} after 1501ms
[server] DELETE /v1/grants/g1 connected at +1918ms (held open)
rest DELETE /v1/grants/g1 -> {"state":"PENDING"} after 1501ms
-- same api object, timeoutMs=200; when the server answers after 1200ms:
rest POST /v1/bots/b1/grants -> {"ok":true} after 1222ms (timeoutMs=200)

timeoutMs = 200: the MCP call and inviteInfo both end at ~200 ms; rest is still pending at 1.5 s and accepts an answer 6× past its deadline.

What to fix

Pass signal: AbortSignal.timeout(timeoutMs) to the fetch at :340 (the shape :378 uses) — the signal covers body reading too — and map AbortError/TimeoutError to the same BotSocialError wording callTool produces (Bot Social whoami: no answer in 0s), so the fallback path reports a timeout instead of hanging.

Scope

In: rest() in plugins/social/src/mcp-client.js and the error mapping around it.

Out: the MCP client's own timeout (:123 already correct), socialFetch's transport, the REST routes themselves, and the callers' retry policy.

Acceptance

  • Against a server that accepts and never answers, rest rejects at about timeoutMs instead of hanging; the test fails with the source change reverted.
  • A rest call whose server answers inside the deadline still resolves with its body.

Activity

  1. added
    bugSomething isn't working
    area:socialFriends, invites, grants, Bot Social
    on Nov 7, 2026
  2. Birfy commented on Nov 7, 2026

    @Birfy
    CollaboratorAuthor

    Written-by: Chief

    Triage: agent-ready (T2).

    • Design conflict: none. inviteInfo at mcp-client.js:375-378 and callTool at :123 already pass AbortSignal.timeout(timeoutMs); rest() at :340 is the one that does not.
    • Scope: rest() in plugins/social/src/mcp-client.js plus its error mapping (AbortError/TimeoutError → the no answer in Ns wording callTool uses). Out: the MCP client's own timeout, socialFetch's transport, the REST routes, and the callers' retry policy.
    • Not hold, not a tracking issue.
    • Admission line: reproducible defect (a server that accepts and never answers leaves rest pending 6× past its deadline).

    First step: pass the signal at :340, then the same held-open server must make rest reject in about timeoutMs.

    Written-by: Chief

  3. added
    agent-readyMaintainer-approved spec: an agent may take it. Only maintainers set this; never in a template
    on Nov 7, 2026
  4. self-assigned this
    on Nov 7, 2026
  5. removed
    agent-readyMaintainer-approved spec: an agent may take it. Only maintainers set this; never in a template
    on Nov 7, 2026
  6. added 2 commits that reference this issue on Nov 9, 2026
    ff7b384
    af9a30b
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

area:socialFriends, invites, grants, Bot SocialbugSomething isn't working

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions