{"record":{"id":"02e58ca2d8b0901f","repo":"garrytan/gstack","slug":"load-html-from-file-waituntil-json-waituntil","errorCode":null,"errorMessage":"load-html: --from-file waitUntil '${json.waitUntil}' invalid","messagePattern":"load-html: --from-file waitUntil '(.+?)' invalid","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"browse/src/write-commands.ts","lineNumber":215,"sourceCode":"          // 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      }\n\n      // Inline HTML path: validate size + magic byte, then setContent directly.\n      if (fromFilePayload) {\n        const MAX_BYTES = parseInt(process.env.GSTACK_BROWSE_MAX_HTML_BYTES || '', 10) || (50 * 1024 * 1024);","sourceCodeStart":197,"sourceCodeEnd":233,"githubUrl":"https://github.com/garrytan/gstack/blob/94993f74012782fd94416dd44b8314f6363a13a4/browse/src/write-commands.ts#L197-L233","documentation":"Validation at write-commands.ts:214-216. The payload's optional waitUntil is present but not one of load, domcontentloaded, networkidle. These are the only Playwright setContent wait strategies the command supports.","triggerScenarios":"Payload JSON like {\"html\": \"...\", \"waitUntil\": \"none\"} or any value outside the allowed set (e.g. 'idle', 'ready', 'complete').","commonSituations":"Copying a waitUntil value from another tool's API; guessing a value; typo.","solutions":["Set waitUntil to 'load', 'domcontentloaded', or 'networkidle'","Omit waitUntil entirely to accept the default (domcontentloaded)"],"exampleFix":"// before — {\"html\":\"...\",\"waitUntil\":\"idle\"}\n// after — {\"html\":\"...\",\"waitUntil\":\"networkidle\"}","handlingStrategy":"validation","validationCode":"const WAIT_UNTIL = new Set(['load','domcontentloaded','networkidle'])\nfunction validPayloadWaitUntil(w: unknown): boolean {\n  return w == null || (typeof w === 'string' && WAIT_UNTIL.has(w))\n}","typeGuard":"function isWaitUntil(v: unknown): v is 'load'|'domcontentloaded'|'networkidle'|undefined {\n  return v == null || (typeof v === 'string' && ['load','domcontentloaded','networkidle'].includes(v))\n}","tryCatchPattern":null,"preventionTips":["Omit waitUntil to accept the default","Restrict to the three Playwright setContent strategies","Validate the payload before loading"],"tags":["load-html","waituntil","validation","write-commands"],"backgroundTag":null,"analyzedSha":"94993f74012782fd94416dd44b8314f6363a13a4","analyzedAt":"2026-08-12T04:06:23.140Z","schemaVersion":2},"datasetVersion":"2026-08-12T13:17:24.610Z"}