paperclipai/paperclip · error · ToolGatewayHttpError
user_authorization_required
user_authorization_required
Error message
User authorization is required
What it means
The connection's credential policy resolved to 'user_authorization_required', meaning a human user's personal authorization is needed before the tool can run, and no user grant exists. If an actingUserId is present the gateway also creates a user-authorization interaction (asking the human to connect), then throws a 409.
Source
Thrown at server/src/services/tool-gateway.ts:3406
eq(companyMemberships.principalType, "user"),
eq(companyMemberships.principalId, userGrant.subjectUserId),
eq(companyMemberships.status, "active"),
)).limit(1);
if (!membership) {
throw new ToolGatewayHttpError(403, "The delegated personal grant owner is not an active company member", "grant_owner_membership_inactive", {
connectionId: connection.id,
grantId: userGrant.id,
});
}
}
}
const resolution = userGrant
? "user"
: resolveCredentialGrantKind(connection.credentialPolicy, actingUserId, false);
if (resolution === "user" && userGrant) return userGrant;
if (resolution === "user_authorization_required") {
if (actingUserId) await createUserAuthorizationInteraction(session, connection, actingUserId);
throw new ToolGatewayHttpError(409, "User authorization is required", "user_authorization_required", {
connectionId: connection.id,
actingUserId,
});
}
return findOrganizationGrant();
}
async function resolveConnectedRemoteTool(session: ToolGatewaySession, tool: ToolGatewayDescriptor) {
if (tool.providerType !== "mcp_remote_http" || !tool.connectionId || !tool.catalogEntryId) {
throw new ToolGatewayHttpError(404, `Tool "${tool.name}" not found`, "tool_not_found");
}
const [entry] = await db
.select()
.from(toolCatalogEntries)
.where(and(
eq(toolCatalogEntries.id, tool.catalogEntryId),
eq(toolCatalogEntries.companyId, session.companyId),
))View on GitHub (pinned to 01ad858492)
Solutions
- Complete the user-authorization interaction: have the acting user connect/authorize the connection in the UI
- Create a user-kind connection grant for the acting user via the connections authorization flow
- Change the connection credentialPolicy to an organization credential if personal grants are not desired
Defensive patterns
Strategy: try-catch
Validate before calling
const grant = await findActiveUserGrant(connection.id, actingUserId);
if (!grant && resolveCredentialGrantKind(connection.credentialPolicy, actingUserId, false) === 'user_authorization_required') {
await createUserAuthorizationInteraction(session, connection, actingUserId); // surface connect prompt first
} Try / catch
try { await runTool(...) } catch (e) { if (e.code === 'user_authorization_required') { await promptUserToAuthorizeConnection(e.details.connectionId); } else throw e; } Prevention
- Check connection authorization status in the UI before launching runs that use it
- Onboard users through the connection flow before assigning them agent work
- Watch for pending user_authorization interactions before scheduling autonomous runs
When it happens
Trigger: A tool on a connection whose credentialPolicy requires per-user authorization is invoked while resolveCredentialGrantKind returns 'user_authorization_required' — no active user-kind grant for the acting user and no delegation supplying userGrant.
Common situations: New user invoking an agent tool before linking their own OAuth account; token revoked/expired leaving no active grant; policy tightened from org-credential to user-credential after deployment.
Related errors
- github_identity_unavailable
- DUPLEX_CHANNEL_CAPABILITY_DENIED
- evaluation_issue_required
- not_authorized
- creator_run_invalid
AI-assisted analysis of paperclipai/paperclip@01ad858492 (2026-09-10).
Data as JSON: /api/errors/36755492157af782.
Report an issue: GitHub.