{"record":{"id":"abb860e4a20c7328","repo":"garrytan/gstack","slug":"denied-qualified-is-not-on-the-cdp-allowlist","errorCode":null,"errorMessage":"DENIED: ${qualified} is not on the CDP allowlist.\nCause: deny-default posture; method has not been audited and added to cdp-allowlist.ts.\nAction: if this method is genuinely needed, open a PR adding it to CDP_ALLOWLIST with a one-line justification + scope (tab|browser) + output (trusted|untrusted).","messagePattern":"DENIED: (.+?) is not on the CDP allowlist\\.\nCause: deny-default posture; method has not been audited and added to cdp-allowlist\\.ts\\.\nAction: if this method is genuinely needed, open a PR adding it to CDP_ALLOWLIST with a one-line justification \\+ scope \\(tab\\|browser\\) \\+ output \\(trusted\\|untrusted\\)\\.","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"browse/src/cdp-bridge.ts","lineNumber":135,"sourceCode":"  raw: unknown;\n  entry: CdpAllowEntry;\n}\n\n/**\n * Look up + acquire mutex + send + release. Throws structured errors on:\n *  - DENIED (method not on allowlist)\n *  - CDPMutexAcquireTimeout (lock contention exceeded budget)\n *  - CDPBridgeTimeout (CDP method itself didn't return in budget)\n *  - CDPSessionInvalidated (Playwright recreated context, session stale)\n */\nexport async function dispatchCdpCall(input: CdpDispatchInput): Promise<CdpDispatchResult> {\n  const qualified = `${input.domain}.${input.method}`;\n  const entry = lookupCdpMethod(qualified);\n  if (!entry) {\n    // Surface the denial via telemetry — this is the data that drives the\n    // next allow-list expansion (DX D9: cdp_method_denied counter).\n    logTelemetry({ event: 'cdp_method_denied', domain: input.domain, method: input.method });\n    throw new Error(\n      `DENIED: ${qualified} is not on the CDP allowlist.\\n` +\n        `Cause: deny-default posture; method has not been audited and added to cdp-allowlist.ts.\\n` +\n        `Action: if this method is genuinely needed, open a PR adding it to CDP_ALLOWLIST with a one-line justification + scope (tab|browser) + output (trusted|untrusted).`\n    );\n  }\n  // Acquire the right tier of lock.\n  const acquireStart = Date.now();\n  const release =\n    entry.scope === 'browser'\n      ? await input.bm.acquireGlobalCdpLock(CDP_ACQUIRE_TIMEOUT_MS)\n      : await input.bm.acquireTabLock(input.tabId, CDP_ACQUIRE_TIMEOUT_MS);\n  const acquireMs = Date.now() - acquireStart;\n  logTelemetry({ event: 'cdp_method_lock_acquire_ms', domain: input.domain, method: input.method, ms: acquireMs });\n  logTelemetry({ event: 'cdp_method_called', domain: input.domain, method: input.method, allowed: true, scope: entry.scope });\n\n  try {\n    const page = input.bm.getPageForTab(input.tabId);\n    if (!page) {","sourceCodeStart":117,"sourceCodeEnd":153,"githubUrl":"https://github.com/garrytan/gstack/blob/94993f74012782fd94416dd44b8314f6363a13a4/browse/src/cdp-bridge.ts#L117-L153","documentation":"Error \"DENIED: ${qualified} is not on the CDP allowlist.\nCause: deny-default posture; method has not been audited and added to cdp-allowlist.ts.\nAction: if this method is genuinely needed, open a PR adding it to CDP_ALLOWLIST with a one-line justification + scope (tab|browser) + output (trusted|untrusted).\" thrown in garrytan/gstack.","triggerScenarios":"Thrown at browse/src/cdp-bridge.ts:135 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"}