{"record":{"id":"7d739486aeacfec3","repo":"garrytan/gstack","slug":"usage-browse-upload-selector-file1-file2","errorCode":null,"errorMessage":"Usage: browse upload <selector> <file1> [file2...]","messagePattern":"Usage: browse upload <selector> <file1> \\[file2\\.\\.\\.\\]","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"warning","filePath":"browse/src/write-commands.ts","lineNumber":596,"sourceCode":"      const sensitiveHeaders = ['authorization', 'cookie', 'set-cookie', 'x-api-key', 'x-auth-token'];\n      const redactedValue = sensitiveHeaders.includes(name.toLowerCase()) ? '****' : value;\n      return `Header set: ${name}: ${redactedValue}`;\n    }\n\n    case 'useragent': {\n      const ua = args.join(' ');\n      if (!ua) throw new Error('Usage: browse useragent <string>');\n      bm.setUserAgent(ua);\n      const error = await bm.recreateContext();\n      if (error) {\n        return `User agent set to \"${ua}\" but: ${error}`;\n      }\n      return `User agent set: ${ua}`;\n    }\n\n    case 'upload': {\n      const [selector, ...filePaths] = args;\n      if (!selector || filePaths.length === 0) throw new Error('Usage: browse upload <selector> <file1> [file2...]');\n\n      // Validate paths are within safe directories (same check as cookie-import)\n      for (const fp of filePaths) {\n        if (!fs.existsSync(fp)) throw new Error(`File not found: ${fp}`);\n        if (path.isAbsolute(fp)) {\n          let resolvedFp: string;\n          try { resolvedFp = fs.realpathSync(path.resolve(fp)); } catch (err: any) { if (err?.code !== 'ENOENT') throw err; resolvedFp = path.resolve(fp); }\n          if (!SAFE_DIRECTORIES.some(dir => isPathWithin(resolvedFp, dir))) {\n            throw new Error(`Path must be within: ${SAFE_DIRECTORIES.join(', ')}`);\n          }\n        }\n        if (path.normalize(fp).includes('..')) {\n          throw new Error('Path traversal sequences (..) are not allowed');\n        }\n      }\n\n      const resolved = await session.resolveRef(selector);\n      if ('locator' in resolved) {","sourceCodeStart":578,"sourceCodeEnd":614,"githubUrl":"https://github.com/garrytan/gstack/blob/94993f74012782fd94416dd44b8314f6363a13a4/browse/src/write-commands.ts#L578-L614","documentation":"Thrown by `browse upload` when no selector is supplied OR when no file paths remain after the selector. The command destructures `[selector, ...filePaths]` from args; both a non-empty selector and at least one file path are mandatory because Playwright's `setInputFiles` needs a target `<input type=file>` and the files to set.","triggerScenarios":"Calling `browse upload` with no args; `browse upload file.txt` (selector omitted, so the file is misread as the selector and filePaths is empty); `browse upload input#file` (selector present but no files).","commonSituations":"An agent resolves the file input selector from a snapshot but the file path argument was dropped during JSON marshalling; a user expects `browse upload file.txt` to auto-locate the input element; the selector was resolved but contained a space and got split, consuming the file path as part of the selector.","solutions":["Provide both a selector and at least one file: `browse upload input[type=file] /tmp/errlookup-0o5UJv/upload.png`.","If the selector contains spaces, quote it so it stays a single arg: `browse upload \"input[data-uploader]\" file.png`.","For multi-file inputs, pass multiple paths: `browse upload #files a.png b.png`.","Confirm the selector targets an `<input type=file>` — `setInputFiles` only works on file inputs."],"exampleFix":"// before\nawait runBrowseCommand(['upload', '/tmp/errlookup-0o5UJv/upload.png']);\n\n// after\nawait runBrowseCommand(['upload', 'input[type=file]', '/tmp/errlookup-0o5UJv/upload.png']);","handlingStrategy":"validation","validationCode":"function validateUploadArgs(args: string[]): { selector: string; filePaths: string[] } {\n  const [selector, ...filePaths] = args;\n  if (!selector || filePaths.length === 0) {\n    throw new Error('upload requires <selector> <file1> [file2...]');\n  }\n  return { selector, filePaths };\n}","typeGuard":"function isUploadArgs(args: unknown): args is [string, string, ...string[]] {\n  return Array.isArray(args) && typeof args[0] === 'string' && args.length >= 2;\n}","tryCatchPattern":null,"preventionTips":["Quote selectors containing spaces so they stay a single arg.","Target an <input type=file> — setInputFiles only works on file inputs.","Pass multiple file paths for multi-file inputs."],"tags":["cli-usage","upload","argument-validation","browse-command"],"backgroundTag":null,"analyzedSha":"94993f74012782fd94416dd44b8314f6363a13a4","analyzedAt":"2026-08-12T04:06:23.140Z","schemaVersion":2},"datasetVersion":"2026-08-12T13:17:24.610Z"}