ComposioHQ/composio · error · Error
Interactive permission prompts are disabled in this environm
Error message
Interactive permission prompts are disabled in this environment (CI/VITEST, or COMPOSIO_DISABLE_PERMISSION_UI); cannot collect an approval for ${params.toolSlug}. What it means
The CLI's interactive permission prompt cannot be shown because the environment is detected as non-interactive (CI, Vitest) or COMPOSIO_DISABLE_PERMISSION_UI is set. The tool-approval flow needs a human decision for the given toolSlug, so it throws rather than silently approving or hanging.
Source
Thrown at ts/packages/cli/src/services/tool-permissions.ts:1047
reject(new Error('Unable to allocate permission callback port.'));
return;
}
void open(`http://127.0.0.1:${port}/?token=${encodeURIComponent(token)}`, {
wait: false,
}).catch(error => {
server.close();
reject(error);
});
});
});
const requestPermissionDecision = async (params: {
readonly toolSlug: string;
readonly accountLabel?: string;
}): Promise<PermissionDecision> => {
if (await Effect.runPromise(isInteractivePermissionUiDisabled)) {
throw new Error(
`Interactive permission prompts are disabled in this environment (CI/VITEST, or COMPOSIO_DISABLE_PERMISSION_UI); cannot collect an approval for ${params.toolSlug}.`
);
}
// Prefer the bundled macOS native sidecar when it is available. The browser
// prompt remains the cross-platform fallback and is only opened when the
// native sidecar is missing or fails before returning a decision.
const nativeDecision = await requestNativeUiPermissionDecision(params).catch(() => undefined);
if (nativeDecision === 'allow_once' || nativeDecision === 'allow_session') return nativeDecision;
if (nativeDecision === 'deny' || nativeDecision === 'dismissed') return 'deny';
return requestPermissionInBrowser({ ...params, agent: await detectNativeUiCallerAgent() });
};
export const gateToolExecution = (params: GateParams) =>
Effect.gen(function* () {
const state = resolveGateState(params);
if (state === 'skip') return;
View on GitHub (pinned to 64b1b85502)
Solutions
- Set an explicit non-interactive permission policy (e.g. allowlist/deny rules or a pre-configured approval mode) so no interactive prompt is needed
- Unset COMPOSIO_DISABLE_PERMISSION_UI and ensure a TTY is available if interactive approval is intended
- In tests, mock/stub the permission decision layer rather than relying on prompts
Example fix
// before await cli.run(commandRequiringApproval); // after process.env.COMPOSIO_TOOL_PERMISSIONS = 'allow'; // or configure allowlist await cli.run(commandRequiringApproval);
Defensive patterns
Strategy: validation
Validate before calling
import { Effect } from 'effect';
const disabled = await Effect.runPromise(isInteractivePermissionUiDisabled);
if (disabled) { /* set non-interactive policy or skip */ } Try / catch
catch (e) { if (/Interactive permission prompts are disabled/.test(String(e.message))) { /* apply fallback policy */ } else throw e; } Prevention
- Configure explicit allow/deny permission rules in CI
- Never rely on interactive prompts in headless environments
When it happens
Trigger: Calling an API that ends in requestPermissionDecision for a tool while running under CI/VITEST env detection or with COMPOSIO_DISABLE_PERMISSION_UI set, without a non-interactive approval policy in place.
Common situations: Running the Composio CLI or its tests in CI pipelines, Docker containers without a TTY, or headless environments where the permission UI was explicitly disabled.
Related errors
- NoStorageAccess
- Local tool ${resolution.finalSlug} is not supported on ${res
- File not readable: {file}. Please check the file permissions
- experimental_subAgent() target must be "claude", "codex", or
- experimental_subAgent() could not determine an agent CLI. Cu
AI-assisted analysis of ComposioHQ/composio@64b1b85502 (2026-08-28).
Data as JSON: /api/errors/518d7b3e5455856a.
Report an issue: GitHub.