Skip to content

create-page/update-page can hang ~4 minutes (255s) on a network hiccup, with no timeout/retry config and no way to tell it apart from a real failure #570

Description

@joshuascottpaul

What is wrong

create-page/update-page (and every other write that goes through wikis/mwnProvider.js's Mwn.init()/new Mwn()) can hang for up to ~255 seconds on a plain network-level failure (timeout, connection reset, DNS blip), with no config surface to shorten it and no way for the MCP caller to distinguish "still working" from "stuck."

Root cause is entirely inside mwn, which this package does not override:

  • mwn/build/bot.js: Mwn.requestDefaults.timeout = 60000 (60s axios timeout).
  • mwn/build/bot.js: defaultOptions.maxRetries = 3, defaultOptions.retryPause = 5000.
  • mwn/build/core.js's Response.prototype.handleRequestFailure: retries any network-level error (no error.response.status, i.e. exactly what a timeout/connection failure looks like) up to maxRetries times, sleeping retryPause between attempts.

Worst case for one network hiccup: (maxRetries + 1) * timeout + maxRetries * retryPause = 4 * 60s + 3 * 5s = 255s (4m15s).

wikis/mwnProvider.js's create() passes only apiUrl, userAgent, and credential fields to Mwn.init(options) — no timeout/maxRetries/retryPause override, and config/loadConfig.js's resolveWiki() doesn't recognize or pass through any request-timing field from config.json either. So there is currently no way to configure this shorter, short of monkeypatching mwn.

Why this matters

runtime/dispatcher.js's runDispatchInner already does the right thing once mwn finally throws — it catches the error and returns a clean, structured MCP error result. The actual problem is that nothing bounds how long mwn is allowed to sit retrying before that happens. In practice, an MCP client (e.g. Claude Desktop/Code) has its own, shorter timeout for a tool call; it gives up and reports "no result received" well before mwn's ~255s retry ladder resolves. The caller then has no idea whether the write eventually succeeded, failed, or is still in flight — and a retry from the caller just stacks another ~255s wait on top if the network issue is still present.

Repro

Reproduced live (2026-08-13) with mediawiki-mcp-server@0.13.0 / mwn@3.0.2, calling getSiteInfo() directly against a black-holed address (http://10.255.255.1, silently dropped, no RST/ICMP):

[0.0s] starting getSiteInfo() against a black-holed address
[W] Retrying in 5 seconds: encountered AxiosError: timeout of 60000ms exceeded   (x3, at ~65s intervals)
[255.1s] FAILED as expected: code=ECONNABORTED message=timeout of 60000ms exceeded

Confirmed wikis/mwnProvider.js is unchanged between 0.13.0 and the current latest (0.17.0) — this affects every shipped version.

Suggested fix

Either (or both):

  1. Expose per-wiki request-timing config. Add optional timeout/maxRetries/retryPause fields to the wiki config schema (config/loadConfig.js's resolveWiki()), plumbed through to Mwn.init(options) in wikis/mwnProvider.js's create() — the same passthrough pattern already used for e.g. oauth2ClientId.
  2. Bound the whole tool call independently of mwn's internal accounting. Wrap tool.handle(args, ctx) in runtime/dispatcher.js's runDispatchInner in an overall timeout (Promise.race against a fixed duration, e.g. 30-45s), returning a clear timeout error to the caller regardless of what mwn is doing in the background. This guarantees a bounded, clear result for any slow upstream, not just this specific retry-ladder cause.

Option 2 is more robust long-term (doesn't depend on tuning mwn's internals correctly), but option 1 is the more direct fix for this specific mechanism and lets an operator dial in values appropriate to their wiki's network conditions.

Local mitigation (for context)

In the interim we're using a NODE_OPTIONS=--import=... preload that shrinks Mwn.requestDefaults.timeout to 25s and self-heals each Mwn instance's maxRetries/retryPause (via wrapping Mwn.prototype.request, gated on the options still being mwn's stock defaults) down to 1/3000, cutting the worst case to ~53s. Happy to share the exact patch if useful, though it's obviously not a substitute for a first-class config option.

Not verified

Whether MediaWiki-side factors (maxlag, session/token refresh) can independently extend this further — the repro above isolates purely the network-level retry path, not the maxlag/assertuserfailed branches in core.js's Response.prototype.process, which use the same retryPause but a separately-bounded retry count.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions