paperclipai/paperclip · error · ToolGatewayHttpError
ambiguous_personal_grant
ambiguous_personal_grant
Error message
More than one delegated personal authorization matches this autonomous run
What it means
The tool gateway resolves a user-scope ('personal') credential grant for an autonomous run by querying connection grant delegations for the session's agent. When more than one active delegated personal grant matches the same connection and agent, the gateway cannot pick one deterministically and throws this 409 instead of guessing.
Source
Thrown at server/src/services/tool-gateway.ts:3379
// accepted from agent input, so a run carrying it uses that owner's grant
// directly. Delegation is reserved for genuinely ownerless unattended runs.
let userGrant = connection.credentialPolicy === "shared" ? undefined : await findUserGrant();
if (!userGrant && !actingUserId && autonomous && session.agentId && connection.credentialPolicy !== "shared") {
const delegated = await db.select({ grant: connectionGrants }).from(connectionGrantDelegations).innerJoin(
connectionGrants,
and(
eq(connectionGrants.id, connectionGrantDelegations.grantId),
eq(connectionGrants.companyId, connectionGrantDelegations.companyId),
),
).where(and(
eq(connectionGrantDelegations.companyId, connection.companyId),
eq(connectionGrantDelegations.agentId, session.agentId),
eq(connectionGrants.connectionId, connection.id),
eq(connectionGrants.kind, "user"),
eq(connectionGrants.status, "active"),
));
if (delegated.length > 1) {
throw new ToolGatewayHttpError(409, "More than one delegated personal authorization matches this autonomous run", "ambiguous_personal_grant", {
connectionId: connection.id,
agentId: session.agentId,
});
}
userGrant = delegated[0]?.grant;
if (userGrant?.subjectUserId) {
const [membership] = await db.select({ id: companyMemberships.id }).from(companyMemberships).where(and(
eq(companyMemberships.companyId, connection.companyId),
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,
});
}View on GitHub (pinned to 01ad858492)
Solutions
- Query connectionGrantDelegations for the agentId/connectionId and revoke or delete all but one active delegation
- Set the older duplicate delegation's status to revoked/inactive via the grants admin API or UI
- Re-run the tool call once a single active delegated personal grant remains
Example fix
// before: duplicate delegations
await db.delete(connectionGrantDelegations).where(and(eq(connectionGrantDelegations.agentId, agentId), eq(connectionGrantDelegations.grantId, staleGrantId)));
// after: keep exactly one active delegation
const keep = delegated[0];
await db.update(connectionGrantDelegations).set({ status: 'revoked' }).where(and(eq(connectionGrantDelegations.agentId, agentId), ne(connectionGrantDelegations.grantId, keep.grantId))); Defensive patterns
Strategy: validation
Validate before calling
const delegated = await db.select().from(connectionGrantDelegations).where(and(eq(connectionGrantDelegations.agentId, agentId), eq(connectionGrantDelegations.grantId, grantId)));
if (delegated.filter(d => d.status === 'active').length > 1) throw new Error('De-duplicate delegations before running'); Prevention
- Revoke existing delegations before re-delegating a connection to the same agent
- Add a unique constraint or upsert on (agentId, connectionId) for active delegations
- Audit delegations after company/agent reconfiguration
When it happens
Trigger: An agent session with agentId set requests a tool bound to a connection with kind 'user' credential policy, and the DB contains >=2 rows in connection_grant_delegations joining to active user-kind connection_grants for that (connectionId, agentId) pair.
Common situations: Re-hiring or re-delegating the same personal connection to the same agent without revoking the previous delegation; a bug or manual DB edit duplicating delegations; company merges that copy grant rows.
Related errors
- github_identity_unavailable
- DUPLEX_CHANNEL_CAPABILITY_DENIED
- A different semantic result was already committed
- native_runner_authority_archive_conflict
- evaluation_issue_required
AI-assisted analysis of paperclipai/paperclip@01ad858492 (2026-09-10).
Data as JSON: /api/errors/59488ddbff34fadf.
Report an issue: GitHub.