{"record":{"id":"4dcd9d38e03aa47e","repo":"can1357/oh-my-pi","slug":"unable-to-safely-override-process-cwd-for-puppetee","errorCode":null,"errorMessage":"Unable to safely override process.cwd for Puppeteer import","messagePattern":"Unable to safely override process\\.cwd for Puppeteer import","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/coding-agent/src/tools/browser/launch.ts","lineNumber":88,"sourceCode":"}\n\nconst STEALTH_ACCEPT_LANGUAGE = \"en-US,en\";\n\nconst USER_AGENT_TARGET_TIMEOUT_MS = 5_000;\nconst USER_AGENT_TARGET_TYPES = new Set([\"page\", \"webview\", \"background_page\"]);\nconst PUPPETEER_SOURCE_URL_SUFFIX = \"//# sourceURL=__puppeteer_evaluation_script__\";\n\n/**\n * Lazy-import puppeteer while hiding the user's cwd from cosmiconfig. The\n * import must not change the filesystem cwd: it is awaited and callers\n * continue running on the main thread while it is pending.\n */\nlet puppeteerCwdFailed = false;\nlet puppeteerCwdFailure: unknown;\nasync function importPuppeteerWithSafeCwd(safeDir: string): Promise<typeof Puppeteer> {\n\tconst cwdDescriptor = Object.getOwnPropertyDescriptor(process, \"cwd\");\n\tif (!cwdDescriptor || typeof cwdDescriptor.value !== \"function\") {\n\t\tthrow new Error(\"Unable to safely override process.cwd for Puppeteer import\");\n\t}\n\n\ttry {\n\t\tObject.defineProperty(process, \"cwd\", { ...cwdDescriptor, value: () => safeDir });\n\t} catch (overrideFailure) {\n\t\tpuppeteerCwdFailed = true;\n\t\tpuppeteerCwdFailure = overrideFailure;\n\t\tthrow overrideFailure;\n\t}\n\tlet loaded: typeof Puppeteer | undefined;\n\tlet importFailure: unknown;\n\tlet importFailed = false;\n\tlet restorationFailure: unknown;\n\tlet restorationFailed = false;\n\ttry {\n\t\ttry {\n\t\t\t// Dynamic import is intentional: Puppeteer probes cwd during module initialization.\n\t\t\tloaded = (await import(\"puppeteer-core\")).default;","sourceCodeStart":70,"sourceCodeEnd":106,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/coding-agent/src/tools/browser/launch.ts#L70-L106","documentation":"Before importing puppeteer-core, importPuppeteerWithSafeCwd temporarily replaces process.cwd with a function returning a safe directory (Puppeteer's cosmiconfig probes cwd during module init). If process.cwd has no own property descriptor whose value is a function — e.g. it was already replaced, deleted, or made non-configurable by other instrumentation — the override is deemed unsafe and this Error is thrown, and the failure is cached (puppeteerCwdFailed) so later imports rethrow it.","triggerScenarios":"Another library/test framework monkey-patched or deleted process.cwd (Object.defineProperty(process,'cwd',{...}) or deletion) before the browser tool first imports puppeteer; bundlers/runtimes exposing a non-configurable cwd; a prior import attempt failed to restore cwd (restorationFailed sets puppeteerCwdFailed).","commonSituations":"Test harnesses that stub process.cwd globally; running under instrumentation that freezes globals; a previous failed puppeteer load poisoning the cached failure for the process lifetime.","solutions":["Find and remove whatever redefines/deletes process.cwd before browser tooling runs (search for defineProperty(process, \"cwd\") / process.cwd = in the host code)","Ensure the original descriptor is restored after any intentional cwd override elsewhere","Restart the process to clear the cached puppeteerCwdFailed failure after fixing the cause","As a workaround, launch against an already-running browser (connected CDP kind) that skips the puppeteer import path if available"],"exampleFix":"// before: test stub without restoring\nprocess.cwd = () => \"/fake\"; // or defineProperty with configurable:false\n// after: patch and restore with a matching descriptor\nconst desc = Object.getOwnPropertyDescriptor(process, \"cwd\")!;\nObject.defineProperty(process, \"cwd\", { ...desc, value: () => \"/fake\" });\n// ...test...\nObject.defineProperty(process, \"cwd\", desc);","handlingStrategy":"try-catch","validationCode":"const d = Object.getOwnPropertyDescriptor(process, \"cwd\");\nif (!d || typeof d.value !== \"function\") {\n  throw new Error(\"process.cwd is not safely patchable; remove global cwd monkey-patching before using the browser tool\");\n}","typeGuard":"function cwdIsPatchable(): boolean {\n  const d = Object.getOwnPropertyDescriptor(process, \"cwd\");\n  return !!d && typeof d.value === \"function\";\n}","tryCatchPattern":"try {\n  await loadPuppeteer();\n} catch (err) {\n  if (err instanceof Error && err.message.includes(\"Unable to safely override process.cwd\")) {\n    // find the library that replaced/deleted process.cwd and remove/repair that patch, then restart\n  }\n  throw err;\n}","preventionTips":["Never stub/delete process.cwd process-wide (especially with configurable:false); scope cwd changes to chdir-free helpers","If you must patch cwd, restore the exact original descriptor in a finally block","Remember the failure is cached for the process lifetime — fix the cause and restart rather than retrying in-process","Run browser tooling before test frameworks apply global environment patches, or order patches so cwd is untouched"],"tags":["environment","monkey-patching","puppeteer","module-loading"],"backgroundTag":"process-global-conflict","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}