nexu-io/open-design · error · ConnectorServiceError
CONNECTOR_SAFETY_DENIED
CONNECTOR_SAFETY_DENIED
Error message
connector tool is not auto-approved read-only by current safety policy
What it means
Thrown by ConnectorService.execute() with HTTP 403 when the connector's effective approval policy (the strictest of definition.minimumApproval, tool.safety.approval, and runtimeSafetyForTool(tool).approval) is not 'auto', OR the tool's runtime side effect is not 'read'. This is the read-only auto-approval gate: only fully-auto read-only tools may run unattended through execute(). details carries approvalPolicy and the full runtime safety object.
Source
Thrown at apps/daemon/src/connectors/service.ts:796
connectorId: request.connectorId,
expectedAccountLabel: request.expectedAccountLabel,
currentAccountLabel: connector.accountLabel ?? null,
});
}
if (!definition.allowedToolNames.includes(request.toolName)) {
throw new ConnectorServiceError('CONNECTOR_TOOL_NOT_FOUND', 'connector tool is not allowed', 404, {
connectorId: request.connectorId,
toolName: request.toolName,
});
}
const tool = definition.tools.find((candidate) => candidate.name === request.toolName);
if (!tool) {
throw new ConnectorServiceError('CONNECTOR_TOOL_NOT_FOUND', 'connector tool not found', 404);
}
const runtimeSafety = runtimeSafetyForTool(tool);
const effectiveApproval = stricterApproval(stricterApproval(definition.minimumApproval, tool.safety.approval), runtimeSafety.approval);
if (effectiveApproval !== 'auto' || runtimeSafety.sideEffect !== 'read') {
throw new ConnectorServiceError('CONNECTOR_SAFETY_DENIED', 'connector tool is not auto-approved read-only by current safety policy', 403, {
connectorId: request.connectorId,
toolName: request.toolName,
approvalPolicy: effectiveApproval ?? null,
safety: { ...runtimeSafety },
});
}
try {
assertJsonSchemaMatches(request.input, tool.inputSchemaJson);
} catch (error) {
throw new ConnectorServiceError('CONNECTOR_INPUT_SCHEMA_MISMATCH', error instanceof Error ? error.message : String(error), 400, {
connectorId: request.connectorId,
toolName: request.toolName,
});
}
this.enforceRunLimits(context);
let providerOutput: BoundedJsonObject;View on GitHub (pinned to 5be4028344)
Solutions
- Obtain explicit user approval for the tool first (the manual approval flow), then retry — do not try to bypass by lowering the policy.
- Switch to a read-only equivalent tool if one exists in allowedToolNames.
- If the tool is genuinely read-only but misclassified, correct tool.safety / the runtime safety mapping in the catalog, not at the call site.
Example fix
// before: auto-invoking a tool that may mutate
await execute({ connectorId, toolName: 'gmail_send', input }, ctx);
// after: route mutating tools through the approval flow
const safety = runtimeSafetyForTool(tool);
if (safety.sideEffect !== 'read' || tool.safety.approval !== 'auto') {
await requestUserApproval(connectorId, toolName, input);
}
await execute({ connectorId, toolName, input }, ctx); Defensive patterns
Strategy: try-catch
Validate before calling
const tool = def.tools.find(t => t.name === request.toolName);
const safety = runtimeSafetyForTool(tool);
const approval = stricterApproval(stricterApproval(def.minimumApproval, tool.safety.approval), safety.approval);
if (approval !== 'auto' || safety.sideEffect !== 'read') {
// route to manual approval flow
await requestUserApproval(request);
}
await connectorService.execute(request, context); Type guard
function isAutoReadOnly(def: ConnectorCatalogDefinition, tool: ConnectorCatalogToolDefinition): boolean {
const safety = runtimeSafetyForTool(tool);
const approval = stricterApproval(stricterApproval(def.minimumApproval, tool.safety.approval), safety.approval);
return approval === 'auto' && safety.sideEffect === 'read';
} Try / catch
try { await connectorService.execute(request, context); }
catch (e) {
if (e instanceof ConnectorServiceError && e.code === 'CONNECTOR_SAFETY_DENIED') {
// surface to user for explicit approval; never auto-retry
} else throw e;
} Prevention
- Classify tools read-only vs mutating in the catalog so the agent can filter proactively.
- Never bypass CONNECTOR_SAFETY_DENIED by retrying — it requires explicit user approval.
- Keep runtimeSafetyForTool mappings aligned with the catalog's safety.approval.
When it happens
Trigger: A tool whose definition or runtime safety requires manual approval (e.g. a write/mutating action like sending email) is invoked through the auto-execute path; or a tool marked read-only at the catalog level resolves to a non-read sideEffect via runtimeSafetyForTool.
Common situations: An agent tried to call a mutating connector tool (send, delete, update) without prior user approval; a connector's minimumApproval was raised from 'auto' to 'manual' by policy; runtimeSafetyForTool flagged a tool as side-effecting despite a stale catalog marking.
Related errors
- CONNECTOR_NOT_CONNECTED
- WORKSPACE_PROJECT_PERMISSION_DENIED
- workspace_resource_share_denied
- CONNECTOR_OUTPUT_TOO_LARGE
- CONNECTOR_NOT_FOUND
AI-assisted analysis of nexu-io/open-design@5be4028344 (2026-08-12).
Data as JSON: /api/errors/0183eb4b8cf7d84c.
Report an issue: GitHub.