paperclipai/paperclip · error · ToolGatewayHttpError
user_secret_missing
user_secret_missing
Error message
Personal credential is not configured
What it means
The user-scoped secret record is valid (scope, owner, and definition all check out), but secrets.resolveUserSecretValue returned no value for the requested definitionId/version/responsibleUserId combination. The gateway throws this 422 to signal that the personal credential itself has not been provisioned yet — the definition exists but no secret value was ever stored (or required=false produced nothing for a required ref).
Solutions
- Have the subject user complete the personal credential setup flow so a value is stored for the user secret definition.
- Check the stored user secret versions for (definitionId, responsibleUserId) and re-add the latest value if it was deleted.
- If the credential is truly optional, set required: false on the credential ref so resolution skips instead of throwing.
- Verify the versionSelector ('latest' vs pinned) matches an existing version.
Example fix
// before: required ref with no user-provided value
resolveUserSecretValue(companyId, { definitionId, responsibleUserId, version: 'latest', required: true })
// after: make the ref optional or provision the value first
resolveUserSecretValue(companyId, { definitionId, responsibleUserId, version: 'latest', required: false }) Defensive patterns
Strategy: validation
Validate before calling
const resolved = await secrets.resolveUserSecretValue(companyId, { definitionId, responsibleUserId, version, required: false });
if (!resolved) {
throw new Error(`User ${responsibleUserId} has no value for secret definition ${definitionId}; complete personal credential setup`);
} Type guard
function hasUserSecret(r: { value: string } | null | undefined): r is { value: string } {
return r !== null && r !== undefined && typeof r.value === 'string' && r.value.length > 0;
} Try / catch
try {
const headers = await resolveCredentialHeaders(session, connection, grant);
} catch (e) {
if (e instanceof ToolGatewayHttpError && e.code === 'user_secret_missing') {
await promptUserToConfigureCredential(grant.subjectUserId, connection.id);
}
throw e;
} Prevention
- Show users a 'connect your account' checklist onboarding so personal credentials are provisioned before tool dispatch.
- Use required: false on optional credential refs so resolution skips instead of throwing.
- Monitor grants whose definitions have zero stored versions and notify owners.
When it happens
Trigger: Calling a tool through a connection whose personal grant references a user secret definition for which the subject user never submitted a value; requesting version 'latest' when no versions exist; the user's stored secret was deleted/revoked while the grant still points at the definition.
Common situations: A new team member is added to a company but never completes the 'connect your account' flow for a tool like Slack or Notion; a user rotates and deletes their personal token without re-authorizing; the version selector is pinned to a specific version that no longer exists.
Related errors
- grant_credential_invalid
- CONNECTOR_BINDING_MISMATCH
- CreateOS requires an API key in the environment config or…
- credentials
- Custom CreateOS API endpoints require an explicit…
AI-assisted analysis of paperclipai/paperclip@3f1d897a7c (2026-09-18).
Data as JSON: /api/errors/0290cf89d7648c3f.
Report an issue: GitHub.
Appendix: source
Thrown at server/src/services/tool-gateway.ts:3546
{
connectionId: connection.id,
grantId: grant.id,
credential: configPath,
},
);
}
const resolved = await secrets.resolveUserSecretValue(
connection.companyId,
{
definitionId: secret.userSecretDefinitionId,
responsibleUserId: grant.subjectUserId,
version: ref.versionSelector ?? "latest",
required: ref.required ?? true,
},
accessContext,
);
if (!resolved) {
throw new ToolGatewayHttpError(
422,
"Personal credential is not configured",
"user_secret_missing",
{
connectionId: connection.id,
grantId: grant.id,
credential: configPath,
},
);
}
return resolved.value;
}
async function maybeRefreshPaperclipCloudGrant(
session: ToolGatewaySession,
connection: typeof toolConnections.$inferSelect,
grant: typeof connectionGrants.$inferSelect,
forceRefresh = false,View on GitHub (pinned to 3f1d897a7c)