can1357/oh-my-pi · warning · ToolError

${interception.message ?? "Command blocked"}

Error message

${interception.message ?? "Command blocked"}

What it means

When bashInterceptor.enabled is on, every command (both the raw command and the cwd-normalized form after a leading `cd ... &&`) is checked against the configured interception rules before execution. If a rule matches and sets block=true, the tool throws with the rule's message, or the generic 'Command blocked' when no custom message is configured. This is an intentional policy gate, not a malfunction.

Source

Thrown at packages/coding-agent/src/tools/bash.ts:941

			if (cd) {
				cwd = cd.path;
				command = cd.rest;
			}
		}
		if (asyncRequested && !this.#asyncEnabled) {
			throw new ToolError("Async bash execution is disabled. Enable async.enabled to use async mode.");
		}

		// Check both the original command and the cwd-normalized command so
		// leading `cd ... &&` wrappers do not hide either shell-navigation rules
		// or the dedicated-tool command that follows the directory change.
		if (this.session.settings.get("bashInterceptor.enabled")) {
			const rules = this.session.settings.getBashInterceptorRules();
			const commandsToCheck = rawCommand === command ? [command] : [rawCommand, command];
			for (const commandToCheck of commandsToCheck) {
				const interception = checkBashInterception(commandToCheck, ctx?.toolNames ?? [], rules, rawCommand);
				if (interception.block) {
					throw new ToolError(interception.message ?? "Command blocked");
				}
			}
		}

		const internalUrlOptions: InternalUrlExpansionOptions = {
			skills: this.session.skills ?? [],
			attachments: this.session.getImageAttachments?.() ?? [],
			internalRouter: InternalUrlRouter.instance(),
			cwd: this.session.cwd,
			sessionFile: this.session.getSessionFile() ?? undefined,
			localOptions: {
				getArtifactsDir: this.session.getArtifactsDir,
				getSessionId: this.session.getSessionId,
			},
		};
		command = await expandInternalUrls(command, { ...internalUrlOptions, ensureLocalParentDirs: true });
		const resolvedEnv = env
			? Object.fromEntries(

View on GitHub (pinned to 9690622007)

Solutions

  1. Read the interception message: it usually names the rule and the sanctioned alternative (dedicated tool or allowed command).
  2. Adjust the command to comply (use the dedicated tool the rule suggests, or restructure the command).
  3. If the block is wrong, fix or remove the rule in the bashInterceptor rules settings, or disable bashInterceptor.enabled.

Example fix

// before: blocked by an interceptor rule
await bash.execute(id, { command: "cd /repo && gh pr merge 12 --force" });

// after: follow the rule's sanctioned path
await prTool.execute(id, { action: "merge", pr: 12 });
Defensive patterns

Strategy: try-catch

Try / catch

try {
  await bash.execute(id, { command });
} catch (e) {
  if (e instanceof ToolError && /blocked/i.test(e.message)) {
    // read the rule's message and switch to the sanctioned alternative
    logger.warn("command blocked by interceptor", { msg: e.message });
  } else throw e;
}

Prevention

When it happens

Trigger: Executing a command matched by a blocking interceptor rule — e.g. rules that forbid `rm -rf`, force redirecting package-manager commands to dedicated tools, or block `git push --force` — including commands hidden behind a leading `cd <dir> &&` wrapper.

Common situations: Teams shipping shared interceptor policies; prompts that generate `cd subdir && npm run dangerous` and expect the rule not to apply; stale rules from previous projects still enabled in settings.

Related errors


AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31). Data as JSON: /api/errors/fb8a5cc224dfa3c3. Report an issue: GitHub.