Skip to content

Raising startupTimeoutMs leaves shutdownTimeoutMs at the 5s default, and the resulting race is reported as a discovery failure #457

Description

@mohanagy

Summary

A profile that raises process.startupTimeoutMs still inherits the 5-second defaultShutdownTimeoutMs. For a slow stdio upstream this asymmetry is a trap: the same child process is given a large budget to start and a very small one to exit. When it misses the shutdown budget, the next start races the dying process and the operator is shown a discovery error that names neither the shutdown nor the timeout that caused it.

Evidence

A wrapper whose upstream is npx firebase-tools@15.26.0 mcp (40–60s cold start, so startupTimeoutMs: 90000, no shutdownTimeoutMs) produced this within one second, on a simultaneous multi-client restart:

03:42:23.740  tools/list        success
03:42:25.121  upstream/shutdown UPSTREAM_SHUTDOWN_TIMEOUT  failure
03:42:25.129  tools/list        UPSTREAM_DISCOVERY_FAILED  failure
03:42:44.890  upstream/start    success
03:42:52.605  tools/list        success

The shutdown timeout fires ~4ms before the discovery failure, and the profile recovers unaided ~27s later.

What the client surfaces is only:

UPSTREAM_DISCOVERY_FAILED: no healthy upstream completed tools discovery for profile 'X':
upstream 'default' (UPSTREAM_TOOL_LIST_FAILED: UPSTREAM_TOOL_LIST_FAILED: unable to list tools for 'X')

Nothing points at shutdown, at a timeout, or at the previous process. UPSTREAM_TOOL_LIST_FAILED is also doubled in the rendered message.

This is not rare. In one operator's audit journal the pattern appears on 26 separate days across ~7 weeks, 187 occurrences, peaking at 19 shutdown timeouts plus 11 discovery failures in a single day.

Contributing factor: each MCP client spawns its own wrapper per service, so several clients restarting together multiply the number of shutdown/start races.

Root cause

src/upstream/upstream-process-manager.ts:24

const defaultShutdownTimeoutMs = 5_000;

startupTimeoutMs and shutdownTimeoutMs are independent optional settings, so raising one leaves the other at 5s. Every one of the ten configurations reviewed set a startup timeout and none set a shutdown timeout.

Suggested directions

  • Derive a shutdown floor from the configured startup timeout, or warn at validation time when startupTimeoutMs greatly exceeds the effective shutdown timeout, so the asymmetry cannot be created silently.
  • Have doctor surface the mismatch as a check.
  • Include the originating condition in the discovery error when a start races a shutdown that timed out, rather than reporting only that discovery failed, and de-duplicate the repeated UPSTREAM_TOOL_LIST_FAILED prefix.

Workaround

Set process.shutdownTimeoutMs explicitly on slow stdio upstreams (20000 for the npx firebase-tools case, 10000 for ordinary stdio children). Verified: doctor reports healthy with DOCTOR_CLEAN_SHUTDOWN passing.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

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