Skip to content

Errors

This page documents common authentication errors and how to resolve them.

OAuth Token Errors

Errors from the /oauth/token endpoint follow OAuth 2.0 error format:

json
{
  "error": "error_code",
  "error_description": "Human-readable description"
}

invalid_request (400)

The request is malformed or missing required parameters.

json
{
  "error": "invalid_request",
  "error_description": "The request is missing required parameters: client_id, client_secret"
}

Causes:

  • Missing grant_type, client_id, or client_secret
  • Wrong Content-Type header (must be application/x-www-form-urlencoded)

Solution:

bash
# Ensure all required parameters are included
curl -X POST https://identity.retailsuccessplatform.com/oauth/token \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d "grant_type=client_credentials" \
  -d "client_id=YOUR_CLIENT_ID" \
  -d "client_secret=YOUR_CLIENT_SECRET"

unsupported_grant_type (400)

The specified grant type is not supported.

json
{
  "error": "unsupported_grant_type",
  "error_description": "The authorization grant type is not supported. Use 'client_credentials'."
}

Causes:

  • Using a grant type other than client_credentials
  • Typo in grant_type parameter

Solution:

bash
# Only client_credentials is supported for service accounts
-d "grant_type=client_credentials"

invalid_client (401)

Client authentication failed.

json
{
  "error": "invalid_client",
  "error_description": "Client authentication failed"
}

Causes:

  • Invalid client_id
  • Invalid client_secret
  • Client secret has been rotated
  • Service account doesn't exist

Solution:

  1. Verify your client_id and client_secret are correct
  2. Check if credentials have been rotated
  3. Contact your administrator to verify the service account exists

client_revoked (401)

The service account has been deactivated.

json
{
  "error": "invalid_client",
  "error_description": "The client has been revoked"
}

Causes:

  • Service account was deactivated by an administrator
  • Service account was deleted

Solution: Contact your administrator to reactivate the service account or create a new one.

invalid_scope (400)

The requested scope is not allowed.

json
{
  "error": "invalid_scope",
  "error_description": "The requested scope is invalid or not authorized"
}

Causes:

  • Requesting a scope not assigned to the service account
  • Typo in scope name
  • Scope doesn't exist

Solution:

bash
# Only request scopes assigned to your service account
# Check your service account configuration for allowed scopes

# Example: if your account has engagehq:content.view
-d "scope=engagehq:content.view"

# Not allowed if you only have view access:
-d "scope=engagehq:content.create"  # Will fail

rate_limit_exceeded (429)

Too many requests in a short period.

json
{
  "error": "rate_limit_exceeded",
  "error_description": "Too many requests. Please wait before trying again.",
  "retry_after": 60
}

Causes:

  • Requesting more than 60 tokens per minute
  • Not caching tokens properly

Solution:

  1. Implement token caching
  2. Wait for the retry_after period
  3. Implement exponential backoff
javascript
// Cache tokens to avoid rate limiting
let cachedToken = null;
let tokenExpiry = 0;

async function getToken() {
  if (cachedToken && Date.now() < tokenExpiry - 300000) {
    return cachedToken;
  }
  // Request new token...
}

API Authentication Errors

Errors when using tokens with API endpoints:

401 Unauthorized

Token is missing, invalid, or expired.

json
{
  "success": false,
  "message": "Unauthenticated."
}

Common Causes:

CauseSolution
Missing Authorization headerAdd Authorization: Bearer {token}
Token expiredRequest a new token
Token malformedCheck for copy/paste errors
Wrong token typeUse Bearer not Basic

Debugging:

javascript
// Check if token is expired
const decoded = decodeToken(token);
const isExpired = decoded.payload.exp * 1000 < Date.now();

if (isExpired) {
  token = await getNewToken();
}

403 Forbidden

Token is valid but lacks required permissions.

json
{
  "success": false,
  "message": "You do not have permission to access this resource."
}

Common Causes:

CauseSolution
Missing required scopeRequest a token with the correct scope
Wrong organization contextVerify organization_id in token
Resource not accessibleCheck resource permissions

Debugging:

javascript
// Check token scopes
const decoded = decodeToken(token);
console.log('Token scopes:', decoded.payload.scopes);
console.log('Token organization:', decoded.payload.organization_id);

Error Handling Best Practices

Retry Logic

javascript
async function makeRequestWithRetry(url, token, maxRetries = 3) {
  for (let attempt = 0; attempt < maxRetries; attempt++) {
    const response = await fetch(url, {
      headers: { 'Authorization': `Bearer ${token}` }
    });

    if (response.ok) {
      return response.json();
    }

    // Handle specific errors
    if (response.status === 401) {
      // Token might be expired, get a new one
      token = await getNewToken();
      continue;
    }

    if (response.status === 429) {
      // Rate limited, wait and retry
      const retryAfter = response.headers.get('Retry-After') || 60;
      await sleep(retryAfter * 1000);
      continue;
    }

    if (response.status >= 500) {
      // Server error, exponential backoff
      await sleep(Math.pow(2, attempt) * 1000);
      continue;
    }

    // Non-retryable error
    throw new Error(`Request failed: ${response.status}`);
  }

  throw new Error('Max retries exceeded');
}

function sleep(ms) {
  return new Promise(resolve => setTimeout(resolve, ms));
}

Token Refresh Strategy

javascript
class TokenManager {
  constructor(clientId, clientSecret) {
    this.clientId = clientId;
    this.clientSecret = clientSecret;
    this.token = null;
    this.expiry = 0;
  }

  async getValidToken() {
    // Refresh if expired or expiring within 5 minutes
    if (!this.token || Date.now() > this.expiry - 300000) {
      await this.refreshToken();
    }
    return this.token;
  }

  async refreshToken() {
    const response = await fetch('/oauth/token', {
      method: 'POST',
      headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
      body: new URLSearchParams({
        grant_type: 'client_credentials',
        client_id: this.clientId,
        client_secret: this.clientSecret,
      })
    });

    if (!response.ok) {
      const error = await response.json();
      throw new TokenError(error.error, error.error_description);
    }

    const data = await response.json();
    this.token = data.access_token;
    this.expiry = Date.now() + (data.expires_in * 1000);
  }
}

class TokenError extends Error {
  constructor(code, description) {
    super(description);
    this.code = code;
  }
}

Logging Errors

javascript
function logAuthError(error, context) {
  console.error('Authentication error:', {
    code: error.code || 'unknown',
    message: error.message,
    context: context,
    timestamp: new Date().toISOString(),
    // Don't log sensitive data
    // clientId: '***', // Redacted
    // token: '***',    // Redacted
  });
}

Error Code Reference

HTTP StatusError CodeDescription
400invalid_requestMissing or invalid parameters
400unsupported_grant_typeWrong grant type
400invalid_scopeScope not allowed
401invalid_clientInvalid credentials
401UnauthenticatedToken invalid/expired
403ForbiddenInsufficient permissions
429rate_limit_exceededToo many requests
500server_errorInternal server error

Getting Help

If you're still experiencing issues:

  1. Check the status page - Verify there are no service outages
  2. Review audit logs - Check for detailed error information
  3. Contact support - Provide error codes and timestamps

Changelog
DateChange
2026-02-27Documentation corrections.
2026-01-15Initial publication.

ShopHero CommerceCore Platform