{"record":{"id":"4fe310b738b7ccac","repo":"different-ai/openwork","slug":"mcp-tool-result-was-incomplete","errorCode":null,"errorMessage":"MCP tool result was incomplete.","messagePattern":"MCP tool result was incomplete\\.","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"ee/apps/den-web/app/(den)/dashboard/_components/mcp-connections-data.tsx","lineNumber":385,"sourceCode":"            typeof payload.message === \"string\" ? payload.message : \"This tool is disabled by organization policy.\",\n            typeof payload.disabledBy === \"string\" ? payload.disabledBy : null,\n            typeof payload.disabledAt === \"string\" ? payload.disabledAt : null,\n          );\n        }\n        const requestError = getRequestError(payload, response, `Failed to run MCP tool (${response.status}).`);\n        throw new ExternalMcpToolRunError(\n          requestError.message,\n          isRecord(payload) ? parseToolCallInspection(payload.inspection) : null,\n          isRecord(payload) ? parseExternalMcpDiagnostic(payload.diagnostic) : null,\n        );\n      }\n      if (\n        !isRecord(payload)\n        || typeof payload.referenceId !== \"string\"\n        || typeof payload.durationMs !== \"number\"\n        || !(\"result\" in payload)\n      ) {\n        throw new Error(\"MCP tool result was incomplete.\");\n      }\n      return {\n        referenceId: payload.referenceId,\n        durationMs: payload.durationMs,\n        result: payload.result,\n        // A missing or unparseable inspection must not fail a tool run that\n        // succeeded (for example across a den-api/den-web deploy skew); the\n        // runner simply renders without the inspector panel.\n        inspection: parseToolCallInspection(payload.inspection),\n      };\n    },\n  });\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n  return typeof value === \"object\" && value !== null;\n}\n","sourceCodeStart":367,"sourceCodeEnd":403,"githubUrl":"https://github.com/different-ai/openwork/blob/2b7df46e8ae1517d64c896c7793d2d52ec845669/ee/apps/den-web/app/(den)/dashboard/_components/mcp-connections-data.tsx#L367-L403","documentation":"After a successful (ok) run-tool response, useRunMcpConnectionTool validates that the payload is a record containing string referenceId, numeric durationMs, and a 'result' key. If any of these is missing/mistyped it throws 'MCP tool result was incomplete.' Notably, a missing/unparseable inspection is tolerated, but the core result envelope is mandatory — without referenceId the run cannot be traced or audited.","triggerScenarios":"Run returns 200 with an empty or differently-shaped body (e.g. {output:...} instead of {referenceId,durationMs,result}); durationMs serialized as a string; server bug omitting result on partial failure while still returning ok.","commonSituations":"den-api version predating the result-envelope contract; gateway rewriting the JSON; tool returning a huge result that got truncated downstream; server returning {ok:true} acknowledgement instead of the full result for async tools.","solutions":["Log the raw response body and compare against the expected { referenceId: string, durationMs: number, result: unknown } envelope","Fix the server route to always return referenceId, durationMs, and result on success","If durationMs is sent as a string, normalize it server-side (or coerce client-side)","Ensure no proxy/middleware truncates or reshapes large JSON responses"],"exampleFix":"// before\nif (!isRecord(payload) || typeof payload.referenceId !== \"string\" || typeof payload.durationMs !== \"number\" || !(\"result\" in payload)) {\n  throw new Error(\"MCP tool result was incomplete.\");\n}\n// after\nconst durationMs = typeof payload?.durationMs === \"string\" ? Number(payload.durationMs) : payload?.durationMs;\nif (!isRecord(payload) || typeof payload.referenceId !== \"string\" || typeof durationMs !== \"number\" || Number.isNaN(durationMs) || !(\"result\" in payload)) {\n  throw new Error(\"MCP tool result was incomplete.\");\n}","handlingStrategy":"validation","validationCode":"function isCompleteToolResult(payload: unknown): payload is { referenceId: string; durationMs: number; result: unknown } {\n  return isRecord(payload) && typeof payload.referenceId === \"string\" && typeof payload.durationMs === \"number\" && \"result\" in payload;\n}","typeGuard":"function isToolResultEnvelope(v: unknown): v is { referenceId: string; durationMs: number; result: unknown } {\n  return isRecord(v) && typeof v.referenceId === \"string\" && typeof v.durationMs === \"number\" && \"result\" in v;\n}","tryCatchPattern":"try {\n  const run = await runTool({ tool, input });\n} catch (err) {\n  if (err.message === \"MCP tool result was incomplete.\") {\n    // keep referenceId-less runs out of history; offer re-run\n  }\n}","preventionTips":["Contract-test the run-tool success envelope {referenceId,durationMs,result}","Always send durationMs as a number from the server","Treat inspection as optional but the envelope as mandatory in server code"],"tags":["api","response-shape","mcp","validation"],"backgroundTag":"unexpected-api-response-shape","analyzedSha":"2b7df46e8ae1517d64c896c7793d2d52ec845669","analyzedAt":"2026-09-01T07:59:23.713Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}