paperclipai/paperclip · error · HttpError
oauth_dynamic_client_registration_failed
oauth_dynamic_client_registration_failed
Error message
OAuth dynamic client registration failed
What it means
Dynamic client registration failure: the registration endpoint returned non-2xx or an invalid document, so no OAuth client could be created for this app on the fly. The provider rejecting the registration request is at fault.
Solutions
- Dynamic client registration failed at the provider. Confirm the provider supports DCR and that the registration request (redirect URIs, scopes) is valid, or pre-register a client and configure its credentials.
Defensive patterns
Strategy: try-catch
When it happens
Trigger: Thrown at server/src/services/tool-access.ts:4260 when the library encounters an invalid state.
Common situations: See trigger scenarios.
AI-assisted analysis of paperclipai/paperclip@3f1d897a7c (2026-08-18).
Data as JSON: /api/errors/619c8e844a02f9e8.
Report an issue: GitHub.
Appendix: source
Thrown at server/src/services/tool-access.ts:6056
row.scope === "company" &&
row.userSecretDefinitionId === null &&
row.key.startsWith(CONNECTION_OWNED_SECRET_KEY_PREFIX);
if (dedicated && !referencedElsewhere.has(secretId)) owned.push(secretId);
else retained.push(secretId);
}
return { owned, retained };
}
/**
* Remove an app: a credential-revoking teardown, not a status flip (PAP-17119).
*
* Order is the security property. Every database-side access path closes
* first — grants, installs, the app-managed profile, gateway tokens minted
* against it, outstanding OAuth state, the catalog, and the connection itself
* — so the app is already undispatchable before the first call out to a secret
* provider. Secret revocation runs last, and each secret's ref survives until
* that secret is gone, so a provider that errors leaves the operation failed
* closed and resumable: the credential is already unresolvable (its row is
* marked deleted first), and retrying the same removal finishes the job.
*
* What stays behind is deliberate: the connection and application rows, their
* ids, names and activity keep working so a later reconnect reuses the same
* identity — but with no credential, no install and no profile, so
* reconnecting has to ask for fresh authentication and rebuild access.
*/
async function removeConnection(
connectionId: string,
companyId?: string,
actor?: ActorInfo,
removalOptions: { confirmComposioChildren?: boolean } = {},
): Promise<ToolConnectionRemovalResult> {
const connection = await getConnectionRow(connectionId, companyId);
const now = new Date();
const binding = actorBinding(actor);
if (isComposioConnection(connection)) {View on GitHub (pinned to 3f1d897a7c)