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

  1. Have the subject user complete the personal credential setup flow so a value is stored for the user secret definition.
  2. Check the stored user secret versions for (definitionId, responsibleUserId) and re-add the latest value if it was deleted.
  3. If the credential is truly optional, set required: false on the credential ref so resolution skips instead of throwing.
  4. 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

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


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)