{"record":{"id":"0290cf89d7648c3f","repo":"paperclipai/paperclip","slug":"user-secret-missing","errorCode":"user_secret_missing","errorMessage":"Personal credential is not configured","messagePattern":"Personal credential is not configured","errorType":"http","errorClass":"ToolGatewayHttpError","httpStatus":422,"severity":"error","filePath":"server/src/services/tool-gateway.ts","lineNumber":3546,"sourceCode":"        {\n          connectionId: connection.id,\n          grantId: grant.id,\n          credential: configPath,\n        },\n      );\n    }\n    const resolved = await secrets.resolveUserSecretValue(\n      connection.companyId,\n      {\n        definitionId: secret.userSecretDefinitionId,\n        responsibleUserId: grant.subjectUserId,\n        version: ref.versionSelector ?? \"latest\",\n        required: ref.required ?? true,\n      },\n      accessContext,\n    );\n    if (!resolved) {\n      throw new ToolGatewayHttpError(\n        422,\n        \"Personal credential is not configured\",\n        \"user_secret_missing\",\n        {\n          connectionId: connection.id,\n          grantId: grant.id,\n          credential: configPath,\n        },\n      );\n    }\n    return resolved.value;\n  }\n\n  async function maybeRefreshPaperclipCloudGrant(\n    session: ToolGatewaySession,\n    connection: typeof toolConnections.$inferSelect,\n    grant: typeof connectionGrants.$inferSelect,\n    forceRefresh = false,","sourceCodeStart":3528,"sourceCodeEnd":3564,"githubUrl":"https://github.com/paperclipai/paperclip/blob/3f1d897a7c018d76563a21c6e39c3c9b03933622/server/src/services/tool-gateway.ts#L3528-L3564","documentation":"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).","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before: required ref with no user-provided value\nresolveUserSecretValue(companyId, { definitionId, responsibleUserId, version: 'latest', required: true })\n// after: make the ref optional or provision the value first\nresolveUserSecretValue(companyId, { definitionId, responsibleUserId, version: 'latest', required: false })","handlingStrategy":"validation","validationCode":"const resolved = await secrets.resolveUserSecretValue(companyId, { definitionId, responsibleUserId, version, required: false });\nif (!resolved) {\n  throw new Error(`User ${responsibleUserId} has no value for secret definition ${definitionId}; complete personal credential setup`);\n}","typeGuard":"function hasUserSecret(r: { value: string } | null | undefined): r is { value: string } {\n  return r !== null && r !== undefined && typeof r.value === 'string' && r.value.length > 0;\n}","tryCatchPattern":"try {\n  const headers = await resolveCredentialHeaders(session, connection, grant);\n} catch (e) {\n  if (e instanceof ToolGatewayHttpError && e.code === 'user_secret_missing') {\n    await promptUserToConfigureCredential(grant.subjectUserId, connection.id);\n  }\n  throw e;\n}","preventionTips":["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."],"tags":["credentials","missing-secret","user-configuration","http-422"],"backgroundTag":"missing-credentials","analyzedSha":"3f1d897a7c018d76563a21c6e39c3c9b03933622","analyzedAt":"2026-09-18T08:03:59.046Z","contentChangedAt":"2026-09-18T08:03:59.046Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}