{"record":{"id":"e294f84af1a9d230","repo":"garrytan/gstack","slug":"load-html-from-file-json-must-have-a-html-str","errorCode":null,"errorMessage":"load-html: --from-file JSON must have a \"html\" string field","messagePattern":"load-html: --from-file JSON must have a \"html\" string field","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"browse/src/write-commands.ts","lineNumber":211,"sourceCode":"          // Parity with the sibling `load-html <file>` path below (line 249):\n          // that branch runs every `file://` target through validateReadPath\n          // so the safe-dirs policy can't be side-stepped. Same policy must\n          // apply here — otherwise --from-file becomes a read-anywhere escape\n          // hatch for any caller that can pick the payload path (e.g., an\n          // MCP caller issuing load-html with an attacker-influenced path).\n          try {\n            validateReadPath(path.resolve(payloadPath));\n          } catch {\n            throw new Error(\n              `load-html: --from-file ${payloadPath} must be under ${SAFE_DIRECTORIES.join(' or ')} (security policy). Copy the payload into the project tree or /tmp first.`\n            );\n          }\n          const raw = fs.readFileSync(payloadPath, 'utf8');\n          let json: any;\n          try { json = JSON.parse(raw); }\n          catch (e: any) { throw new Error(`load-html: --from-file JSON parse failed: ${e.message}`); }\n          if (typeof json.html !== 'string') {\n            throw new Error('load-html: --from-file JSON must have a \"html\" string field');\n          }\n          if (json.waitUntil && json.waitUntil !== 'load'\n              && json.waitUntil !== 'domcontentloaded' && json.waitUntil !== 'networkidle') {\n            throw new Error(`load-html: --from-file waitUntil '${json.waitUntil}' invalid`);\n          }\n          fromFilePayload = { html: json.html, waitUntil: json.waitUntil };\n        } else if (args[i] === '--wait-until') {\n          const val = args[++i];\n          if (val !== 'load' && val !== 'domcontentloaded' && val !== 'networkidle') {\n            throw new Error(`Invalid --wait-until '${val}'. Must be one of: load, domcontentloaded, networkidle.`);\n          }\n          waitUntil = val;\n        } else if (args[i].startsWith('--')) {\n          throw new Error(`Unknown flag: ${args[i]}`);\n        } else if (!filePath) {\n          filePath = args[i];\n        }\n      }","sourceCodeStart":193,"sourceCodeEnd":229,"githubUrl":"https://github.com/garrytan/gstack/blob/94993f74012782fd94416dd44b8314f6363a13a4/browse/src/write-commands.ts#L193-L229","documentation":"Schema guard at write-commands.ts:211. The payload parsed as valid JSON, but json.html is missing or not a string (number/array/object/null/undefined). The contract requires a top-level string field literally named 'html'.","triggerScenarios":"JSON without an html key; html set to a non-string; wrong key name (content, body, markup, source); html set to a file path string is actually fine type-wise but semantically wrong — but a missing/typed-wrong html triggers this.","commonSituations":"Using a different key name by convention; html set to null or an array of nodes; an empty {} object.","solutions":["Ensure the top-level object has html as a string: {\"html\": \"<html>...</html>\"}","Check the exact key spelling and casing","Confirm the value is the markup string itself, not a path or structured object"],"exampleFix":"// before — {\"content\": \"<html>...\"}  → no html field\n// after\n{\"html\": \"<html>...\"}","handlingStrategy":"type-guard","validationCode":"function hasHtmlString(json: unknown): json is { html: string } {\n  return typeof (json as any)?.html === 'string'\n}","typeGuard":"function isFromFilePayload(x: unknown): x is { html: string; waitUntil?: string } {\n  return typeof (x as any)?.html === 'string'\n}","tryCatchPattern":null,"preventionTips":["Always include a top-level string field named exactly 'html'","Validate the parsed shape before handing it to the command","Confirm the value is markup, not a path or object"],"tags":["json","load-html","schema","write-commands"],"backgroundTag":null,"analyzedSha":"94993f74012782fd94416dd44b8314f6363a13a4","analyzedAt":"2026-08-12T04:06:23.140Z","schemaVersion":2},"datasetVersion":"2026-08-12T13:17:24.610Z"}