different-ai/openwork · error · ProbeFailure
SHUTDOWN
SHUTDOWN
Error message
Session DELETE returned HTTP ${deleteResponse.status} What it means
During cleanup the probe DELETEs the negotiated MCP session (expecting HTTP 204 per the MCP streamable-HTTP session lifecycle). When the caller set requireValidDelete (strict shutdown validation), any status other than 204 is raised as a SHUTDOWN ProbeFailure naming the actual status. With non-strict cleanup the DELETE failure is swallowed so it never masks a primary diagnostic failure.
Source
Thrown at packages/enterprise-mcp-mock-server/src/testing/probe.ts:447
let revocationClientSecret = options.credentials?.clientSecret ?? ""
let negotiatedProtocolHeader = ""
let revocationEndpoint: URL | null = null
const cleanup = async (requireValidDelete: boolean): Promise<void> => {
const cleanupDeadline = Date.now() + 2_000
if (sessionId && accessToken && negotiatedProtocolHeader) {
try {
const deleteResponse = await fetchStep(mcpUrl, {
method: "DELETE",
headers: {
authorization: `Bearer ${accessToken}`,
origin: baseUrl.origin,
"mcp-session-id": sessionId,
"mcp-protocol-version": negotiatedProtocolHeader,
},
}, "SHUTDOWN", cleanupDeadline)
await discardResponseBody(deleteResponse, "SHUTDOWN", "mcp_session")
if (requireValidDelete && deleteResponse.status !== 204) {
throw new ProbeFailure("SHUTDOWN", "mcp_session", `Session DELETE returned HTTP ${deleteResponse.status}`)
}
} catch (error) {
if (requireValidDelete) throw error
}
}
if (revocationEndpoint && accessToken) {
try {
const revocationResponse = await fetchStep(revocationEndpoint, {
method: "POST",
headers: { "content-type": "application/x-www-form-urlencoded" },
body: new URLSearchParams({
token: accessToken,
client_id: revocationClientId,
...(revocationClientSecret ? { client_secret: revocationClientSecret } : {}),
}),
}, "SHUTDOWN", cleanupDeadline)
await discardResponseBody(revocationResponse, "SHUTDOWN", "oauth_token")
} catch {View on GitHub (pinned to 2b7df46e8a)
Solutions
- Make the server return HTTP 204 with no body for authenticated session DELETE requests.
- Check for middleware/proxies that intercept DELETE and return 405/200, and exempt the MCP endpoint.
- If you do not need strict teardown conformance, run the probe in a mode that does not require a valid DELETE so cleanup failures are non-fatal.
- Ensure the mcp-session-id header sent on DELETE matches the session established during initialize (no stale session reuse).
Example fix
// before (server route)
app.delete(endpointPath, (req, res) => { sessions.delete(req.headers["mcp-session-id"]); res.sendStatus(200) })
// after
app.delete(endpointPath, (req, res) => { sessions.delete(req.headers["mcp-session-id"]); res.sendStatus(204) }) Defensive patterns
Strategy: try-catch
Try / catch
try {
const result = await probeEnterpriseMcpMockServer({ baseUrl, mode: "fixture-conformance" })
} catch (e) {
if (e instanceof ProbeFailure && e.phase === "SHUTDOWN") {
// Session teardown issue; primary phases may still have passed.
console.warn("Non-fatal shutdown issue:", e.message)
} else throw e
} Prevention
- Make session DELETE handlers return exactly 204 with no body
- Test session teardown (DELETE with mcp-session-id) in CI against your MCP server
- Ensure proxies/middleware allow DELETE through to the MCP route
- Never reuse or pre-expire sessions before the probe's cleanup runs
When it happens
Trigger: Running the probe in a mode where valid session teardown is required and the server responds to DELETE /<endpointPath> with mcp-session-id and Bearer token using a status like 200, 404 (session already gone), or 405 instead of 204.
Common situations: Targeting a non-conformant or older MCP server that returns 200 on session delete, a gateway/proxy stripping the DELETE or rewriting it, or a server that already expired the session so DELETE returns 404.
Related errors
- MCP_SHUTDOWN_FAILED
- MCP endpoint returned HTTP ${methodResponse.status} to GET
- AUTH_RESOURCE_DISCOVERY
- Failed to load ${connection.name}'s apps (${response.status}
- Failed to configure plugin connection (${response.status}).
AI-assisted analysis of different-ai/openwork@2b7df46e8a (2026-09-01).
Data as JSON: /api/errors/1aebd2dc32a565ae.
Report an issue: GitHub.