{"record":{"id":"0fde9f49ed3acd4d","repo":"garrytan/gstack","slug":"usage-browse-useragent-string","errorCode":null,"errorMessage":"Usage: browse useragent <string>","messagePattern":"Usage: browse useragent <string>","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"warning","filePath":"browse/src/write-commands.ts","lineNumber":585,"sourceCode":"      }]);\n      return `Cookie set: ${name}=****`;\n    }\n\n    case 'header': {\n      const headerStr = args[0];\n      if (!headerStr || !headerStr.includes(':')) throw new Error('Usage: browse header <name>:<value>');\n      const sep = headerStr.indexOf(':');\n      const name = headerStr.slice(0, sep).trim();\n      const value = headerStr.slice(sep + 1).trim();\n      await bm.setExtraHeader(name, value);\n      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); }","sourceCodeStart":567,"sourceCodeEnd":603,"githubUrl":"https://github.com/garrytan/gstack/blob/94993f74012782fd94416dd44b8314f6363a13a4/browse/src/write-commands.ts#L567-L603","documentation":"Thrown by `browse useragent` when the joined args string is empty. The command joins all positional args with spaces to form the user agent string; if no args were supplied, `ua` is `''` which is falsy, and the command refuses to set an empty user agent (which Playwright would treat as a deliberate override to empty, breaking fingerprinting and likely site behavior).","triggerScenarios":"Calling `browse useragent` with no arguments; passing only whitespace tokens that join to an empty string; forwarding a UA variable that resolved to empty.","commonSituations":"An agent reads `navigator.userAgent`, intends to re-apply it after context recreation, but the read happened before navigation completed and returned empty; a user wants to RESET to the default UA and assumes omitting the value does that (it does not — there is no reset path through this command); copy-paste from a config where the UA string lived on the next line and was dropped.","solutions":["Provide a full UA string, quoting it so the shell does not split it: `browse useragent \"Mozilla/5.0 ...\"`.","If you want the default Playwright UA back, recreate the context without a custom UA via the appropriate BrowserManager path rather than passing empty.","Capture the UA from `navigator.userAgent` BEFORE any context recreation so the value is populated.","If scripting, assert the UA variable is non-empty before dispatching the command."],"exampleFix":"// before\nconst ua = readUaMaybe(); // may be ''\nawait runBrowseCommand(['useragent', ua]);\n\n// after\nconst ua = await page.evaluate(() => navigator.userAgent);\nif (!ua) throw new Error('UA read failed');\nawait runBrowseCommand(['useragent', ua]);","handlingStrategy":"validation","validationCode":"function validateUserAgentArg(args: string[]): string {\n  const ua = args.join(' ').trim();\n  if (!ua) throw new Error('useragent requires a non-empty UA string');\n  return ua;\n}","typeGuard":"function isNonEmptyString(s: unknown): s is string {\n  return typeof s === 'string' && s.trim().length > 0;\n}","tryCatchPattern":null,"preventionTips":["Quote the full UA string so the shell does not split it.","Capture navigator.userAgent before context recreation, not after.","There is no reset-to-default via this command — use the BrowserManager context path instead."],"tags":["cli-usage","user-agent","argument-validation","browse-command"],"backgroundTag":null,"analyzedSha":"94993f74012782fd94416dd44b8314f6363a13a4","analyzedAt":"2026-08-12T04:06:23.140Z","schemaVersion":2},"datasetVersion":"2026-08-12T13:17:24.610Z"}