paperclipai/paperclip · error · PaperclipCloudConnectorError
REAUTHORIZATION_REQUIRED
REAUTHORIZATION_REQUIRED
Error message
Paperclip Cloud scope grant did not match
What it means
The stored Paperclip Cloud credential's granted scopes no longer match the scopes required by the connector profile definition (sameStringSet(credentials.scopes, definition.scopes) fails). This is thrown by openCredentials, which validates every stored binding before use by claim/refresh. The code is REAUTHORIZATION_REQUIRED, meaning the user must re-run the authorization flow to obtain a fresh scope grant.
Source
Thrown at server/src/services/paperclip-cloud-connector.ts:309
config.environment,
definition.provider,
profile,
definition.scopes,
);
if (
credentials.instanceId !== config.instanceId
|| credentials.environment !== config.environment
|| credentials.subject !== subject
|| credentials.companyId !== companyId
|| credentials.provider !== definition.provider
) {
throw new PaperclipCloudConnectorError("Paperclip Cloud credential binding did not match", "CONNECTOR_BINDING_MISMATCH");
}
if (credentials.profile !== profile) {
throw new PaperclipCloudConnectorError("Paperclip Cloud connector profile binding did not match", "CONNECTOR_BINDING_MISMATCH");
}
if (!sameStringSet(credentials.scopes, definition.scopes)) {
throw new PaperclipCloudConnectorError("Paperclip Cloud scope grant did not match", "REAUTHORIZATION_REQUIRED");
}
return credentials;
}
return {
async getInstanceStatus(): Promise<"active" | "suspended" | "removed"> {
let response: ConnectorResponse;
try {
response = await call("status", {
subject: "instance-status",
companyId: "instance-status",
});
} catch (error) {
if (error instanceof PaperclipCloudConnectorError && error.status === 401) return "removed";
throw error;
}
if (response.status === "active" && response.active === true) return "active";
if (response.status === "suspended" && response.active === false) return "suspended";View on GitHub (pinned to 01ad858492)
Solutions
- Re-run the authorization flow (startAuthorization) so the user grants the current definition scopes
- Diff credentials.scopes against definition.scopes to identify which scope is missing/extra, then update the connector definition or re-consent
- If the definition change was unintentional, revert the scope list in the connector definition so it matches existing grants
- Verify the broker is returning the full requested scope set and not a reduced one
Example fix
// before (stale grant after definition change)
const creds = await connector.claim({ subject, companyId }); // throws REAUTHORIZATION_REQUIRED
// after (detect and re-authorize)
try {
const creds = await connector.claim({ subject, companyId });
} catch (e) {
if (e instanceof PaperclipCloudConnectorError && e.code === "REAUTHORIZATION_REQUIRED") {
const session = await connector.startAuthorization({ subject, companyId, profile, returnUri, returnState });
// redirect user to session.confirmationUrl to grant the new scopes
} else { throw e; }
} Defensive patterns
Strategy: try-catch
Validate before calling
// cannot inspect stored scopes without the claim call; detect the mismatch instead
function isScopeMismatch(e: unknown): boolean {
return e instanceof PaperclipCloudConnectorError && e.code === "REAUTHORIZATION_REQUIRED";
} Type guard
function isPaperclipCloudConnectorError(e: unknown): e is PaperclipCloudConnectorError {
return e instanceof PaperclipCloudConnectorError && typeof e.code === "string";
} Try / catch
try {
const creds = await connector.claim({ subject, companyId });
} catch (e) {
if (isPaperclipCloudConnectorError(e) && e.code === "REAUTHORIZATION_REQUIRED") {
const session = await connector.startAuthorization({ subject, companyId, profile, returnUri, returnState });
// redirect user to session.confirmationUrl
} else throw e;
} Prevention
- Keep connector definition scopes stable; bump them only with a planned re-consent migration
- On deploy, diff definition.scopes against previously granted scopes and notify users to re-authorize
- Prefer additive scope changes and request the superset up front
When it happens
Trigger: openCredentials is called (via claim or refresh) while the stored credentials.scopes set differs from definition.scopes — e.g. the connector definition was updated to add/remove a scope after the user originally authorized, or the broker returned a partial grant at authorization time.
Common situations: A deploy upgraded the connector profile (e.g. gmail.draft gained an extra Gmail scope) while users hold old credentials; a broker-side policy change narrowed the granted scopes; credentials were provisioned by an older connector version with a different scope list.
Related errors
- connector_reauthorization_required
- connector_profile_invalid
- OAuth callback connection belongs to a missing company
- Discord bot is not installed in the selected server
- Managed GitHub connection is unavailable
AI-assisted analysis of paperclipai/paperclip@01ad858492 (2026-09-10).
Data as JSON: /api/errors/cc266580e031f607.
Report an issue: GitHub.