{"record":{"id":"ad844f9b4535b112","repo":"google-gemini/gemini-cli","slug":"shell-command-confirmation-required","errorCode":null,"errorMessage":"Shell command confirmation required","messagePattern":"Shell command confirmation required","errorType":"exception","errorClass":"ConfirmationRequiredError","httpStatus":null,"severity":"error","filePath":"packages/cli/src/services/prompt-processors/shellProcessor.ts","lineNumber":146,"sourceCode":"        {\n          name: 'run_shell_command',\n          args: { command },\n        },\n        undefined,\n      );\n\n      if (decision === PolicyDecision.DENY) {\n        throw new Error(\n          `${this.commandName} cannot be run. Blocked command: \"${command}\". Reason: Blocked by policy.`,\n        );\n      } else if (decision === PolicyDecision.ASK_USER) {\n        commandsToConfirm.add(command);\n      }\n    }\n\n    // Handle confirmation requirements.\n    if (commandsToConfirm.size > 0) {\n      throw new ConfirmationRequiredError(\n        'Shell command confirmation required',\n        Array.from(commandsToConfirm),\n      );\n    }\n\n    let processedPrompt = '';\n    let lastIndex = 0;\n\n    for (const injection of resolvedInjections) {\n      // Append the text segment BEFORE the injection, substituting {{args}} with RAW input.\n      const segment = prompt.substring(lastIndex, injection.startIndex);\n      processedPrompt += segment.replaceAll(\n        SHORTHAND_ARGS_PLACEHOLDER,\n        userArgsRaw,\n      );\n\n      // Execute the resolved command (which already has ESCAPED input).\n      if (injection.resolvedCommand) {","sourceCodeStart":128,"sourceCodeEnd":164,"githubUrl":"https://github.com/google-gemini/gemini-cli/blob/5024443c7217464a66e98f80d73172a26440bd8f/packages/cli/src/services/prompt-processors/shellProcessor.ts#L128-L164","documentation":"Thrown as a ConfirmationRequiredError when one or more resolved shell commands received PolicyDecision.ASK_USER. The error carries commandsToConfirm (the list of commands needing approval). In contexts without an interactive confirmation flow this aborts; in interactive mode it drives the confirmation UI.","triggerScenarios":"The policy engine returns ASK_USER for at least one '!{...}' command that is not on the session shell allowlist, so commandsToConfirm is non-empty and ConfirmationRequiredError is thrown.","commonSituations":"A shell injection command is neither allowed nor denied, so policy asks the user; running in non-interactive mode where there is no way to ask; first use of a command that has not yet been approved/allowlisted.","solutions":["Run in interactive mode and approve the command when prompted (approval can add it to the allowlist).","Pre-populate the session shell allowlist or an allow policy rule for the command so it no longer triggers ASK_USER.","Catch ConfirmationRequiredError in programmatic callers and present the commandsToConfirm list to your own approval flow, then retry with an updated allowlist."],"exampleFix":"// programmatic handling\ntry {\n  await shellProcessor.process(prompt, ctx);\n} catch (e) {\n  if (e instanceof ConfirmationRequiredError) {\n    showApprovals(e.commandsToConfirm);\n  } else throw e;\n}","handlingStrategy":"try-catch","validationCode":"async function needsConfirmation(commands: string[], config: Config): Promise<string[]> {\n  const out: string[] = [];\n  for (const c of commands) {\n    const { decision } = await config.getPolicyEngine().check({ name: 'run_shell_command', args: { command: c } }, undefined);\n    if (decision === PolicyDecision.ASK_USER) out.push(c);\n  }\n  return out;\n}","typeGuard":"import { ConfirmationRequiredError } from './shellProcessor.js';\nfunction isConfirmationRequired(e: unknown): e is ConfirmationRequiredError {\n  return e instanceof ConfirmationRequiredError;\n}","tryCatchPattern":"try {\n  await shellProcessor.process(prompt, ctx);\n} catch (e) {\n  if (e instanceof ConfirmationRequiredError) {\n    // e.commandsToConfirm lists commands needing approval; run your own approval flow,\n    // add approved ones to session.sessionShellAllowlist, then retry.\n  } else throw e;\n}","preventionTips":["Pre-populate the shell allowlist for commands you intend to run unattended.","In interactive mode, approve once to allowlist recurring commands.","Catch ConfirmationRequiredError in programmatic callers and drive a custom approval UI."],"tags":["shell","policy","confirmation","security","permissions"],"backgroundTag":null,"analyzedSha":"5024443c7217464a66e98f80d73172a26440bd8f","analyzedAt":"2026-08-12T06:01:53.711Z","schemaVersion":2},"datasetVersion":"2026-08-12T12:31:55.035Z"}