different-ai/openwork · error · ExternalMcpToolRunError
Failed to run MCP tool (${response.status}).
Error message
Failed to run MCP tool (${response.status}). What it means
When a run-tool request fails with any non-403-policy status, the hook builds the message via getRequestError(payload, response, `Failed to run MCP tool (${response.status}).`) and wraps it in ExternalMcpToolRunError together with any inspection and diagnostic payloads the server attached. The template string is only the fallback — the real message comes from the response body — so this error represents a concrete server-side tool execution failure with attribution data.
Source
Thrown at ee/apps/den-web/app/(den)/dashboard/_components/mcp-connections-data.tsx:373
const { response, payload } = await requestJson(
`/v1/mcp-connections/${encodeURIComponent(connectionId)}/tools/call`,
{
method: "POST",
headers: getOrgScopeHeaders(requireOrgId(orgId)),
body: JSON.stringify(input),
},
RUN_TOOL_REQUEST_TIMEOUT_MS,
);
if (!response.ok) {
if (response.status === 403 && isRecord(payload) && payload.error === "policy_blocked") {
throw new ExternalMcpToolPolicyBlockedError(
typeof payload.message === "string" ? payload.message : "This tool is disabled by organization policy.",
typeof payload.disabledBy === "string" ? payload.disabledBy : null,
typeof payload.disabledAt === "string" ? payload.disabledAt : null,
);
}
const requestError = getRequestError(payload, response, `Failed to run MCP tool (${response.status}).`);
throw new ExternalMcpToolRunError(
requestError.message,
isRecord(payload) ? parseToolCallInspection(payload.inspection) : null,
isRecord(payload) ? parseExternalMcpDiagnostic(payload.diagnostic) : null,
);
}
if (
!isRecord(payload)
|| typeof payload.referenceId !== "string"
|| typeof payload.durationMs !== "number"
|| !("result" in payload)
) {
throw new Error("MCP tool result was incomplete.");
}
return {
referenceId: payload.referenceId,
durationMs: payload.durationMs,
result: payload.result,
// A missing or unparseable inspection must not fail a tool run thatView on GitHub (pinned to 2b7df46e8a)
Solutions
- Read error.inspection and error.diagnostic on ExternalMcpToolRunError for the server-side failure attribution
- Check the connection's credentialHealth; re-authorize if reconnect_required
- Validate the tool input against the catalog's inputSchema before calling
- Retry only after fixing the underlying cause (connectivity, credentials, or input)
Example fix
// before
await runTool({ tool: name, input });
// after
try {
await runTool({ tool: name, input });
} catch (err) {
if (err instanceof ExternalMcpToolRunError) {
console.error("Tool run failed:", err.message, err.inspection, err.diagnostic);
}
throw err;
} Defensive patterns
Strategy: try-catch
Validate before calling
function isValidToolInput(tool: ExternalMcpTool, input: unknown): boolean {
// validate against tool.inputSchema (JSON Schema) before the run call
return validateJsonSchema(input, tool.inputSchema);
} Type guard
function isToolRunError(e: unknown): e is ExternalMcpToolRunError {
return e instanceof ExternalMcpToolRunError;
} Try / catch
try {
await runTool({ tool, input });
} catch (err) {
if (err instanceof ExternalMcpToolRunError) {
console.error(err.message, { inspection: err.inspection, diagnostic: err.diagnostic });
// route 401/403 to reconnect flow, 5xx to retry-after-check
}
} Prevention
- Validate tool input against the catalog inputSchema before invoking
- Monitor connection credentialHealth and prompt reconnect proactively
- Use inspection/diagnostic fields for user-facing attribution instead of retrying blindly
When it happens
Trigger: MCP server unreachable or returned an error; auth to the external MCP failed (401/403 non-policy); tool input rejected (4xx); upstream timeout (504) within RUN_TOOL_REQUEST_TIMEOUT_MS; any non-ok status other than the 403 policy_blocked case.
Common situations: Expired OAuth credentials on the connection (needs reconnect); wrong tool input schema; external MCP server down or DNS/network failure from the den server; per-member credential mode where the calling member never connected.
Related errors
- the hostname does not resolve
- MCP_TOOL_REPORTED_ERROR
- MCP_TOOL_EXECUTION
- Connection closed before the tool response completed
- Could not start ${action.connectionName} authorization.
AI-assisted analysis of different-ai/openwork@2b7df46e8a (2026-09-01).
Data as JSON: /api/errors/b4aa22c623fea5e9.
Report an issue: GitHub.