{"record":{"id":"1f0aaf0737a18254","repo":"different-ai/openwork","slug":"mcp-app-sandbox-resource-invalid","errorCode":"MCP_APP_SANDBOX_RESOURCE_INVALID","errorMessage":"The sandbox received an invalid HTML resource payload.","messagePattern":"The sandbox received an invalid HTML resource payload\\.","errorType":"error_code","errorClass":null,"httpStatus":null,"severity":"error","filePath":"apps/server/src/mcp-app-sandbox.ts","lineNumber":109,"sourceCode":"      readyState = inner.contentDocument?.readyState || null;\n      hasHtmlRoot = Boolean(inner.contentDocument?.documentElement);\n      scriptCount = inner.contentDocument?.scripts.length ?? null;\n    } catch {}\n    notifyHost(\"ui/notifications/sandbox-resource-loaded\", { readyState, hasHtmlRoot, scriptCount });\n  });\n  inner.addEventListener(\"error\", () => {\n    if (resourceAssigned) notifyHost(\"ui/notifications/sandbox-diagnostic\", { code: \"MCP_APP_SANDBOX_DOCUMENT_ERROR\", message: \"The sandbox iframe reported a document load error.\" });\n  });\n  document.body.appendChild(inner);\n  window.addEventListener(\"message\", (event) => {\n    if (event.source === window.parent) {\n      if (event.origin !== hostOrigin) return;\n      if (event.data?.method === \"ui/notifications/sandbox-resource-ready\") {\n        const html = event.data?.params?.html;\n        const sandbox = event.data?.params?.sandbox;\n        if (typeof sandbox === \"string\" && /^(?:allow-scripts|allow-same-origin|\\s)+$/.test(sandbox)) inner.setAttribute(\"sandbox\", sandbox);\n        if (typeof html !== \"string\") {\n          notifyHost(\"ui/notifications/sandbox-diagnostic\", { code: \"MCP_APP_SANDBOX_RESOURCE_INVALID\", message: \"The sandbox received an invalid HTML resource payload.\" });\n          return;\n        }\n        try {\n          resourceAssigned = true;\n          inner.srcdoc = html;\n          notifyHost(\"ui/notifications/sandbox-resource-accepted\");\n        } catch {\n          notifyHost(\"ui/notifications/sandbox-diagnostic\", { code: \"MCP_APP_SANDBOX_RESOURCE_ASSIGNMENT_FAILED\", message: \"The sandbox could not assign the HTML resource to its isolated document.\" });\n        }\n        return;\n      }\n      inner.contentWindow?.postMessage(event.data, \"*\");\n      return;\n    }\n    if (event.source === inner.contentWindow && event.origin === ownOrigin) {\n      window.parent.postMessage(event.data, hostTargetOrigin);\n    }\n  });","sourceCodeStart":91,"sourceCodeEnd":127,"githubUrl":"https://github.com/different-ai/openwork/blob/2b7df46e8ae1517d64c896c7793d2d52ec845669/apps/server/src/mcp-app-sandbox.ts#L91-L127","documentation":"When the sandbox receives the ui/notifications/sandbox-resource-ready message from the host, the params.html field must be a string to be assigned to inner.srcdoc. If html is missing or not a string, the sandbox reports diagnostic MCP_APP_SANDBOX_RESOURCE_INVALID and ignores the message, so the iframe stays blank.","triggerScenarios":"Host posts { method: \"ui/notifications/sandbox-resource-ready\", params: {} } with no html, or with html as a Buffer/object/undefined — e.g. the host serialization step failed or the message was constructed with a different params shape.","commonSituations":"Host and sandbox protocol version mismatch (one side renamed html field); binary/encoded HTML passed instead of a string; host forwarding a resource payload where fetch failed and undefined leaked through.","solutions":["Log event.data.params on the host side before posting and confirm html is a non-empty string.","Fix the host sender so the fetched resource string is passed as params.html (stringify/decode if needed).","Align host and sandbox versions so the message protocol (field names) matches.","Add a host-side guard: only post sandbox-resource-ready when typeof html === 'string'."],"exampleFix":"// before\nnotify({ method: \"ui/notifications/sandbox-resource-ready\", params: { html: await resource.text() ?? undefined } });\n// after\nconst html = await resource.text();\nif (typeof html === \"string\") notify({ method: \"ui/notifications/sandbox-resource-ready\", params: { html } });","handlingStrategy":"type-guard","validationCode":null,"typeGuard":"function isValidResource(m: unknown): m is { method: \"ui/notifications/sandbox-resource-ready\"; params: { html: string; sandbox?: string } } {\n  const d = m as any;\n  return d?.method === \"ui/notifications/sandbox-resource-ready\" && typeof d?.params?.html === \"string\";\n}","tryCatchPattern":"window.addEventListener(\"message\", (e) => {\n  if (e.data?.code === \"MCP_APP_SANDBOX_RESOURCE_INVALID\") {\n    // re-request the resource from the host or surface a host-side error\n  }\n});","preventionTips":["Type the host→sandbox message protocol in shared code so params.html is always string","Validate the resource string on the host before posting sandbox-resource-ready","Version the host/sandbox message contract"],"tags":["iframe","sandbox","payload-validation","mcp-apps"],"backgroundTag":"invalid-payload","analyzedSha":"2b7df46e8ae1517d64c896c7793d2d52ec845669","analyzedAt":"2026-09-01T07:59:23.713Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}