Skip to main content

Error Handling

MergeGuide API uses standard HTTP status codes and provides detailed error responses.

Error Response Format

All errors return a consistent JSON structure:
{
  "error": "error_type",
  "message": "Human-readable error message",
  "code": "SPECIFIC_ERROR_CODE",
  "details": {
    "field": "Additional context"
  },
  "request_id": "req_abc123"
}

HTTP Status Codes

CodeMeaning
200Success
201Created
204No Content (successful deletion)
400Bad Request - Invalid input
401Unauthorized - Authentication required
403Forbidden - Insufficient permissions
404Not Found - Resource doesn’t exist
409Conflict - Resource already exists
422Unprocessable Entity - Validation failed
429Too Many Requests - Rate limited
500Internal Server Error
503Service Unavailable

Error Types

Authentication Errors (401)

{
  "error": "unauthorized",
  "message": "Invalid or expired API key",
  "code": "AUTH_INVALID_KEY"
}
CodeDescription
AUTH_MISSING_KEYNo API key provided
AUTH_INVALID_KEYKey format is invalid
AUTH_EXPIRED_KEYKey has expired
AUTH_REVOKED_KEYKey was revoked

Permission Errors (403)

{
  "error": "forbidden",
  "message": "Insufficient permissions for this operation",
  "code": "AUTH_INSUFFICIENT_SCOPE",
  "details": {
    "required_scope": "write:policies",
    "current_scopes": ["read:policies"]
  }
}
CodeDescription
AUTH_INSUFFICIENT_SCOPEKey lacks required scope
AUTH_IP_NOT_ALLOWEDIP not in allowlist
AUTH_ORG_ACCESS_DENIEDNot a member of organization

Validation Errors (400, 422)

{
  "error": "validation_error",
  "message": "Request validation failed",
  "code": "VALIDATION_FAILED",
  "details": {
    "errors": [
      {
        "field": "repository",
        "message": "Repository name is required",
        "code": "REQUIRED"
      },
      {
        "field": "files[0].content",
        "message": "File content exceeds maximum size",
        "code": "MAX_SIZE_EXCEEDED",
        "max": 1048576
      }
    ]
  }
}
CodeDescription
REQUIREDField is required
INVALID_FORMATValue format is invalid
MAX_SIZE_EXCEEDEDValue exceeds maximum
MIN_SIZE_REQUIREDValue below minimum
INVALID_ENUMValue not in allowed list

Resource Errors (404, 409)

{
  "error": "not_found",
  "message": "Evaluation not found",
  "code": "RESOURCE_NOT_FOUND",
  "details": {
    "resource_type": "evaluation",
    "resource_id": "eval_abc123"
  }
}
CodeDescription
RESOURCE_NOT_FOUNDResource doesn’t exist
RESOURCE_ALREADY_EXISTSDuplicate resource
RESOURCE_DELETEDResource was deleted

Rate Limit Errors (429)

{
  "error": "rate_limited",
  "message": "Rate limit exceeded",
  "code": "RATE_LIMIT_EXCEEDED",
  "details": {
    "limit": 100,
    "window": "1m",
    "retry_after": 45
  }
}
Headers included:
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1705311600
Retry-After: 45

Server Errors (500, 503)

{
  "error": "internal_error",
  "message": "An unexpected error occurred",
  "code": "INTERNAL_ERROR",
  "request_id": "req_abc123"
}
CodeDescription
INTERNAL_ERRORUnexpected server error
SERVICE_UNAVAILABLEService temporarily down
DEPENDENCY_FAILUREExternal service failed

Handling Errors

JavaScript/TypeScript

import { MergeGuideClient, MergeGuideError } from '@mergeguide/sdk';

const client = new MergeGuideClient({ apiKey: process.env.MERGEGUIDE_API_KEY });

try {
  const evaluation = await client.evaluations.create({
    repository: 'owner/repo',
    files: [...]
  });
} catch (error) {
  if (error instanceof MergeGuideError) {
    switch (error.code) {
      case 'AUTH_INVALID_KEY':
        console.error('Invalid API key, please check configuration');
        break;
      case 'RATE_LIMIT_EXCEEDED':
        const retryAfter = error.details.retry_after;
        console.log(`Rate limited, retrying in ${retryAfter}s`);
        await sleep(retryAfter * 1000);
        // Retry request
        break;
      case 'VALIDATION_FAILED':
        console.error('Validation errors:', error.details.errors);
        break;
      default:
        console.error(`API error: ${error.message}`);
    }
  } else {
    // Network or other error
    throw error;
  }
}

Python

from mergeguide import MergeGuideClient, MergeGuideError
import time

client = MergeGuideClient(api_key=os.environ['MERGEGUIDE_API_KEY'])

try:
    evaluation = client.evaluations.create(
        repository='owner/repo',
        files=[...]
    )
except MergeGuideError as e:
    if e.code == 'AUTH_INVALID_KEY':
        print('Invalid API key')
    elif e.code == 'RATE_LIMIT_EXCEEDED':
        retry_after = e.details.get('retry_after', 60)
        time.sleep(retry_after)
        # Retry request
    elif e.code == 'VALIDATION_FAILED':
        for err in e.details.get('errors', []):
            print(f"Field {err['field']}: {err['message']}")
    else:
        raise

cURL/Shell

response=$(curl -s -w "\n%{http_code}" \
  -H "Authorization: Bearer $MERGEGUIDE_API_KEY" \
  https://api.mergeguide.ai/v1/evaluations/eval_abc123)

http_code=$(echo "$response" | tail -n1)
body=$(echo "$response" | sed '$d')

case $http_code in
  200)
    echo "Success: $body"
    ;;
  401)
    echo "Authentication failed"
    exit 1
    ;;
  429)
    retry_after=$(echo "$body" | jq -r '.details.retry_after')
    echo "Rate limited, waiting ${retry_after}s"
    sleep $retry_after
    # Retry
    ;;
  *)
    echo "Error $http_code: $(echo "$body" | jq -r '.message')"
    exit 1
    ;;
esac

Retry Strategy

async function withRetry<T>(
  fn: () => Promise<T>,
  maxRetries = 3
): Promise<T> {
  let lastError: Error;

  for (let attempt = 0; attempt < maxRetries; attempt++) {
    try {
      return await fn();
    } catch (error) {
      lastError = error;

      if (error instanceof MergeGuideError) {
        // Don't retry client errors
        if (error.status >= 400 && error.status < 500) {
          throw error;
        }

        // Handle rate limiting
        if (error.code === 'RATE_LIMIT_EXCEEDED') {
          const retryAfter = error.details.retry_after || 60;
          await sleep(retryAfter * 1000);
          continue;
        }
      }

      // Exponential backoff for server errors
      const delay = Math.min(1000 * Math.pow(2, attempt), 30000);
      await sleep(delay);
    }
  }

  throw lastError;
}

Retryable Errors

CodeRetryableStrategy
429YesUse Retry-After header
500YesExponential backoff
503YesExponential backoff
400NoFix request
401NoCheck credentials
403NoCheck permissions
404NoCheck resource ID

Debugging

Request ID

Every response includes a request_id. Include this when contacting support:
{
  "error": "internal_error",
  "message": "An unexpected error occurred",
  "request_id": "req_abc123def456"
}

Debug Mode

Enable verbose logging in SDKs:
const client = new MergeGuideClient({
  apiKey: process.env.MERGEGUIDE_API_KEY,
  debug: true  // Logs requests and responses
});

Common Issues

IssueSolution
Intermittent 401Check key expiration, rotate keys
Consistent 403Verify key scopes match operation
422 on createCheck all required fields present
429 frequentlyImplement request batching