{"record":{"id":"33bd4514ed7b2f47","repo":"garrytan/gstack","slug":"buildfetchhandler-cfg-authtoken-must-be-a-non-emp","errorCode":null,"errorMessage":"buildFetchHandler: cfg.authToken must be a non-empty string >= 16 chars","messagePattern":"buildFetchHandler: cfg\\.authToken must be a non-empty string >= 16 chars","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"browse/src/server.ts","lineNumber":1471,"sourceCode":" * routes via cfg.beforeRoute, pass a pre-launched cfg.browserManager, and\n * opt out of terminal-agent teardown via cfg.ownsTerminalAgent (default\n * true, set to false when the embedder runs its own PTY server). The CLI\n * path calls this through start() with env-derived defaults and explicit\n * cfg.ownsTerminalAgent: true — externally-observable behavior is identical.\n *\n * Auth state lives ENTIRELY inside the factory closure: cfg.authToken is the\n * single source of truth for the bearer secret, factory-scoped validateAuth\n * closes over it, and factory-scoped shutdown closes the cfg-provided\n * browserManager. Module-level lifecycle singletons (LOCAL_LISTEN_PORT,\n * tunnelActive, inspector state) intentionally STAY at module scope; see\n * the v1.35.0.0 CHANGELOG entry for the architectural rationale.\n *\n * The returned ServerHandle is callable directly. Bun.serve is the caller's\n * responsibility — embedders may fd-pass; CLI uses Bun.serve normally.\n */\nexport function buildFetchHandler(cfg: ServerConfig): ServerHandle {\n  if (!cfg.authToken || cfg.authToken.length < 16) {\n    throw new Error('buildFetchHandler: cfg.authToken must be a non-empty string >= 16 chars');\n  }\n  if (!cfg.browserManager) {\n    throw new Error('buildFetchHandler: cfg.browserManager is required');\n  }\n\n  // Re-run init with cfg-provided values. ensureStateDir is idempotent\n  // (mkdir -p); initAuditLog is idempotent (sets a module string);\n  // initRegistry is idempotent for same-token, throws for different-token.\n  // Owning init here (instead of at module load) means cfg.authToken is the\n  // single source of truth for the registry root token.\n  ensureStateDir(cfg.config);\n  initAuditLog(cfg.config.auditLog);\n  initRegistry(cfg.authToken);\n\n  const { authToken, browserManager: cfgBrowserManager, startTime, beforeRoute, browsePort } = cfg;\n  // Strict opt-out: only explicit `false` flips the gate. Any other value\n  // (undefined, truthy non-bool from a JS caller bypassing TS, etc.) defaults\n  // to gstack-owns. Matches the \"default-true preserves CLI bit-for-bit\"","sourceCodeStart":1453,"sourceCodeEnd":1489,"githubUrl":"https://github.com/garrytan/gstack/blob/94993f74012782fd94416dd44b8314f6363a13a4/browse/src/server.ts#L1453-L1489","documentation":"Error \"buildFetchHandler: cfg.authToken must be a non-empty string >= 16 chars\" thrown in garrytan/gstack.","triggerScenarios":"Thrown at browse/src/server.ts:1471 when the library encounters an invalid state.","commonSituations":"See trigger scenarios.","solutions":[],"exampleFix":null,"handlingStrategy":null,"validationCode":null,"typeGuard":null,"tryCatchPattern":null,"preventionTips":[],"tags":[],"backgroundTag":null,"analyzedSha":"94993f74012782fd94416dd44b8314f6363a13a4","analyzedAt":"2026-08-12T04:06:23.140Z","schemaVersion":2},"datasetVersion":"2026-08-12T13:17:24.610Z"}