{"record":{"id":"dabfb0c34fdb2c8c","repo":"denoland/deno","slug":"pledge-test-permissions-called-before-restoring-pr","errorCode":null,"errorMessage":"pledge test permissions called before restoring previous pledge","messagePattern":"pledge test permissions called before restoring previous pledge","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"cli/ops/bench.rs","lineNumber":64,"sourceCode":"  state.borrow::<ModuleSpecifier>().to_string()\n}\n\n#[derive(Clone)]\nstruct PermissionsHolder(Uuid, PermissionsContainer);\n\n#[op2(stack_trace)]\n#[serde]\npub fn op_pledge_test_permissions(\n  state: &mut OpState,\n  #[serde] args: ChildPermissionsArg,\n) -> Result<Uuid, deno_runtime::deno_permissions::ChildPermissionError> {\n  let token = Uuid::new_v4();\n  let parent_permissions = state.borrow_mut::<PermissionsContainer>();\n  let worker_permissions = parent_permissions.create_child_permissions(args)?;\n  let parent_permissions = parent_permissions.clone();\n\n  if state.try_take::<PermissionsHolder>().is_some() {\n    panic!(\"pledge test permissions called before restoring previous pledge\");\n  }\n\n  state.put::<PermissionsHolder>(PermissionsHolder(token, parent_permissions));\n\n  // NOTE: This call overrides current permission set for the worker\n  state.put::<PermissionsContainer>(worker_permissions);\n\n  Ok(token)\n}\n\n#[op2]\npub fn op_restore_test_permissions(\n  state: &mut OpState,\n  #[serde] token: Uuid,\n) -> Result<(), JsErrorBox> {\n  match state.try_take::<PermissionsHolder>() {\n    Some(permissions_holder) => {\n      if token != permissions_holder.0 {","sourceCodeStart":46,"sourceCodeEnd":82,"githubUrl":"https://github.com/denoland/deno/blob/9ad36f7a2cce60488e6ec52283efb32efddaf93a/cli/ops/bench.rs#L46-L82","documentation":"Panic in the op_pledge_test_permissions op (cli/ops/bench.rs) backing `deno bench`. The test harness 'pledges' (temporarily replaces) the worker's permission set and stores the original in OpState under PermissionsHolder. Pledging again while a previous pledge is still active finds the stored holder and panics, because only one nested permission swap is supported.","triggerScenarios":"Calling the internal pledge op twice without an intervening restore — in practice, a custom/forked test runner (or Deno's own harness code misordered) invoking op_pledge_test_permissions before op_restore_test_permissions completed.","commonSituations":"Contributors modifying cli/js bench/testing harness logic; user code reaching into internal ops via unstable internals; a race where one bench worker re-enters the harness entrypoint. Not reachable through normal Deno.bench() usage.","solutions":["Restore before pledging again: always pair each pledge with op_restore_test_permissions(token) in a finally block","Review custom harness code that calls these ops and fix the call ordering","If this fires from stock `deno bench` usage, report it as a Deno bug with a minimal reproduction"],"exampleFix":"// before (harness pseudo-code)\nconst t1 = pledge(args);\nconst t2 = pledge(args);          // panics: previous pledge active\n// after\nconst t1 = pledge(args);\ntry { runBench(); } finally { restore(t1); }\nconst t2 = pledge(args);","handlingStrategy":"validation","validationCode":"// wrap the internal ops so a second pledge is refused before it panics\nconst state = { pledged: false };\nfunction safePledge(args) {\n  if (state.pledged) throw new Error('previous pledge not restored');\n  const token = Deno[Deno.internal].pledgeTestPermissions(args);\n  state.pledged = true;\n  return token;\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Maintain strict pledge/restore pairing in harness code, restoring in finally blocks","Never call the pledge op from concurrent paths in one worker"],"tags":["permissions","bench","internal-ops","state-machine","panic"],"backgroundTag":"invalid-operation-sequence","analyzedSha":"9ad36f7a2cce60488e6ec52283efb32efddaf93a","analyzedAt":"2026-08-20T13:07:44.778Z","contentChangedAt":"2026-08-20T13:07:44.778Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}