Skip to main content

Error Codes

This page documents all error codes returned by the FireBackup API. Understanding these errors helps you handle failures gracefully and provide meaningful feedback to users.

Error Response Format​

All API errors follow a consistent JSON format:

{
"success": false,
"error": {
"code": "ERROR_CODE",
"message": "Human-readable error message",
"details": {
// Optional additional context
}
}
}
FieldTypeDescription
successbooleanAlways false for errors
error.codestringMachine-readable error code
error.messagestringHuman-readable description
error.detailsobjectAdditional context (optional)

HTTP Status Codes​

StatusDescriptionCommon Causes
400Bad RequestInvalid input, validation errors
401UnauthorizedMissing or invalid token
403ForbiddenInsufficient permissions
404Not FoundResource doesn't exist
409ConflictDuplicate resource, state conflict
422Unprocessable EntityValid JSON but invalid semantics
429Too Many RequestsRate limit exceeded
500Internal Server ErrorServer-side failure
502Bad GatewayUpstream service failure
503Service UnavailableMaintenance or overload

Authentication Errors​

UNAUTHORIZED​

Status: 401

{
"success": false,
"error": {
"code": "UNAUTHORIZED",
"message": "Authentication required"
}
}

Causes:

  • Missing Authorization header
  • Token not provided

Resolution: Include a valid Bearer token in the Authorization header.


INVALID_TOKEN​

Status: 401

{
"success": false,
"error": {
"code": "INVALID_TOKEN",
"message": "Invalid or malformed token"
}
}

Causes:

  • Malformed JWT
  • Invalid signature
  • Token from different issuer

Resolution: Obtain a new token through the authentication flow.


TOKEN_EXPIRED​

Status: 401

{
"success": false,
"error": {
"code": "TOKEN_EXPIRED",
"message": "Token has expired",
"details": {
"expiredAt": "2024-01-15T10:00:00Z"
}
}
}

Causes:

  • JWT past expiration time
  • Session timeout

Resolution: Refresh the token or re-authenticate.


INVALID_REFRESH_TOKEN​

Status: 401

{
"success": false,
"error": {
"code": "INVALID_REFRESH_TOKEN",
"message": "Invalid or expired refresh token"
}
}

Causes:

  • Refresh token revoked
  • Refresh token expired
  • Token already used (one-time use)

Resolution: Re-authenticate through the full OAuth flow.

Authorization Errors​

PERMISSION_DENIED​

Status: 403

{
"success": false,
"error": {
"code": "PERMISSION_DENIED",
"message": "You don't have permission to perform this action",
"details": {
"requiredRole": "admin",
"currentRole": "member",
"requiredPermission": "backups:delete"
}
}
}

Causes:

  • User role insufficient for operation
  • Resource belongs to another organization

Resolution: Request elevated permissions from an admin.


ORGANIZATION_ACCESS_DENIED​

Status: 403

{
"success": false,
"error": {
"code": "ORGANIZATION_ACCESS_DENIED",
"message": "You are not a member of this organization"
}
}

Causes:

  • User not in organization
  • Organization ID mismatch
  • Membership revoked

Resolution: Verify organization membership or use correct organization ID.


MISSING_ORGANIZATION_HEADER​

Status: 400

{
"success": false,
"error": {
"code": "MISSING_ORGANIZATION_HEADER",
"message": "X-Organization-Id header is required"
}
}

Causes:

  • Header not provided
  • Empty header value

Resolution: Include X-Organization-Id header with valid organization ID.

Resource Errors​

PROJECT_NOT_FOUND​

Status: 404

{
"success": false,
"error": {
"code": "PROJECT_NOT_FOUND",
"message": "Project with ID 'proj_xxx' not found"
}
}

Causes:

  • Project doesn't exist
  • Project deleted
  • Wrong organization context

Resolution: Verify project ID and organization context.


BACKUP_NOT_FOUND​

Status: 404

{
"success": false,
"error": {
"code": "BACKUP_NOT_FOUND",
"message": "Backup 'bkp_xxx' not found"
}
}

Causes:

  • Backup doesn't exist
  • Backup deleted
  • Wrong project context

Resolution: Verify backup ID exists for the specified project.


SCHEDULE_NOT_FOUND​

Status: 404

{
"success": false,
"error": {
"code": "SCHEDULE_NOT_FOUND",
"message": "Schedule 'sched_xxx' not found"
}
}

STORAGE_NOT_FOUND​

Status: 404

{
"success": false,
"error": {
"code": "STORAGE_NOT_FOUND",
"message": "Storage destination not found"
}
}

WEBHOOK_NOT_FOUND​

Status: 404

{
"success": false,
"error": {
"code": "WEBHOOK_NOT_FOUND",
"message": "Webhook not found"
}
}

ORGANIZATION_NOT_FOUND​

Status: 404

{
"success": false,
"error": {
"code": "ORGANIZATION_NOT_FOUND",
"message": "Organization not found"
}
}

PITR_NOT_ENABLED​

Status: 400

{
"success": false,
"error": {
"code": "PITR_NOT_ENABLED",
"message": "PITR is not enabled for this project"
}
}

Resolution: Enable PITR using the /pitr/enable endpoint.

Validation Errors​

VALIDATION_ERROR​

Status: 400

{
"success": false,
"error": {
"code": "VALIDATION_ERROR",
"message": "Validation failed",
"details": {
"errors": [
{
"field": "email",
"message": "Invalid email format"
},
{
"field": "name",
"message": "Name must be at least 2 characters"
}
]
}
}
}

Causes:

  • Missing required fields
  • Invalid field format
  • Value out of range

Resolution: Check the details.errors array for specific field issues.


INVALID_CRON​

Status: 400

{
"success": false,
"error": {
"code": "INVALID_CRON",
"message": "Invalid CRON expression",
"details": {
"expression": "invalid * * * *",
"field": "minute",
"reason": "Expected number or special character"
}
}
}

Resolution: Use a valid 5-field CRON expression (minute hour day month weekday).


INVALID_TIMESTAMP​

Status: 400

{
"success": false,
"error": {
"code": "INVALID_TIMESTAMP",
"message": "Requested timestamp is outside the retention window",
"details": {
"requestedTimestamp": "2024-01-01T00:00:00Z",
"oldestAvailable": "2024-01-08T10:00:00Z",
"latestAvailable": "2024-01-15T10:30:00Z"
}
}
}

Resolution: Use a timestamp within the PITR retention window.

Operation Errors​

BACKUP_IN_PROGRESS​

Status: 409

{
"success": false,
"error": {
"code": "BACKUP_IN_PROGRESS",
"message": "A backup is already in progress for this project",
"details": {
"activeBackupId": "bkp_abc123",
"startedAt": "2024-01-15T10:00:00Z"
}
}
}

Resolution: Wait for the current backup to complete or cancel it.


RESTORE_IN_PROGRESS​

Status: 409

{
"success": false,
"error": {
"code": "RESTORE_IN_PROGRESS",
"message": "A restore operation is already in progress",
"details": {
"activeRestoreId": "restore_abc123"
}
}
}

RESTORE_FAILED​

Status: 500

{
"success": false,
"error": {
"code": "RESTORE_FAILED",
"message": "Failed to restore backup",
"details": {
"reason": "Insufficient permissions on target project",
"documentsRestored": 5000,
"failedAt": "users/user_5001"
}
}
}

Resolution: Check target project permissions and retry.


SCHEDULE_CONFLICT​

Status: 409

{
"success": false,
"error": {
"code": "SCHEDULE_CONFLICT",
"message": "A schedule already exists for this project at the same time"
}
}

Resolution: Use a different CRON expression or modify the existing schedule.

Firebase/GCP Errors​

INVALID_CREDENTIALS​

Status: 400

{
"success": false,
"error": {
"code": "INVALID_CREDENTIALS",
"message": "Failed to connect to Firebase project",
"details": {
"reason": "Token expired or revoked"
}
}
}

Resolution: Re-authenticate the Firebase project connection.


IAM_PERMISSION_DENIED​

Status: 403

{
"success": false,
"error": {
"code": "IAM_PERMISSION_DENIED",
"message": "Missing required IAM permissions",
"details": {
"missing": [
"roles/datastore.importExportAdmin",
"roles/firebase.viewer"
]
}
}
}

Resolution: Grant the required IAM roles in Google Cloud Console.


AUDIT_LOGS_REQUIRED​

Status: 400

{
"success": false,
"error": {
"code": "AUDIT_LOGS_REQUIRED",
"message": "Firebase audit logs must be enabled for PITR",
"details": {
"instructions": "Enable audit logs in Google Cloud Console"
}
}
}

Resolution: Enable Firestore audit logs in GCP IAM & Admin > Audit Logs.


FIREBASE_CONNECTION_FAILED​

Status: 502

{
"success": false,
"error": {
"code": "FIREBASE_CONNECTION_FAILED",
"message": "Failed to connect to Firebase",
"details": {
"firebaseProjectId": "my-project",
"reason": "Network timeout"
}
}
}

Resolution: Check network connectivity and Firebase project status.

Storage Errors​

BUCKET_NOT_FOUND​

Status: 404

{
"success": false,
"error": {
"code": "BUCKET_NOT_FOUND",
"message": "Bucket 'my-bucket' does not exist or is not accessible"
}
}

STORAGE_ACCESS_DENIED​

Status: 403

{
"success": false,
"error": {
"code": "STORAGE_ACCESS_DENIED",
"message": "Access denied to storage bucket",
"details": {
"bucket": "my-bucket",
"operation": "write"
}
}
}

Resolution: Verify storage credentials have read/write permissions.


FILE_NOT_ORPHAN​

Status: 400

{
"success": false,
"error": {
"code": "FILE_NOT_ORPHAN",
"message": "File is associated with a backup and cannot be deleted directly"
}
}

Resolution: Delete the backup record first to orphan the file.


STORAGE_UPLOAD_FAILED​

Status: 500

{
"success": false,
"error": {
"code": "STORAGE_UPLOAD_FAILED",
"message": "Failed to upload backup to storage",
"details": {
"storageType": "S3",
"bucket": "my-bucket",
"reason": "Request timeout"
}
}
}

Organization Errors​

INVITATION_EXISTS​

Status: 409

{
"success": false,
"error": {
"code": "INVITATION_EXISTS",
"message": "An invitation already exists for this email"
}
}

Resolution: Cancel the existing invitation or wait for it to expire.


INVITATION_EXPIRED​

Status: 400

{
"success": false,
"error": {
"code": "INVITATION_EXPIRED",
"message": "This invitation has expired",
"details": {
"expiredAt": "2024-01-15T00:00:00Z"
}
}
}

Resolution: Request a new invitation from an organization admin.


CANNOT_REMOVE_OWNER​

Status: 400

{
"success": false,
"error": {
"code": "CANNOT_REMOVE_OWNER",
"message": "Cannot remove the organization owner. Transfer ownership first."
}
}

Resolution: Transfer ownership to another member before leaving.


MEMBER_EXISTS​

Status: 409

{
"success": false,
"error": {
"code": "MEMBER_EXISTS",
"message": "User is already a member of this organization"
}
}

LAST_ADMIN​

Status: 400

{
"success": false,
"error": {
"code": "LAST_ADMIN",
"message": "Cannot remove the last admin. Promote another member first."
}
}

Rate Limiting​

RATE_LIMIT_EXCEEDED​

Status: 429

{
"success": false,
"error": {
"code": "RATE_LIMIT_EXCEEDED",
"message": "Too many requests",
"details": {
"limit": 100,
"window": "1m",
"retryAfter": 45
}
}
}

Headers:

Retry-After: 45
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1705312845

Resolution: Wait for retryAfter seconds before retrying.

Server Errors​

INTERNAL_ERROR​

Status: 500

{
"success": false,
"error": {
"code": "INTERNAL_ERROR",
"message": "An unexpected error occurred",
"details": {
"requestId": "req_abc123xyz"
}
}
}

Resolution: Retry the request. If persistent, contact support with the requestId.


SERVICE_UNAVAILABLE​

Status: 503

{
"success": false,
"error": {
"code": "SERVICE_UNAVAILABLE",
"message": "Service temporarily unavailable",
"details": {
"reason": "maintenance",
"estimatedRecovery": "2024-01-15T12:00:00Z"
}
}
}

Resolution: Wait for scheduled maintenance to complete.


DATABASE_ERROR​

Status: 500

{
"success": false,
"error": {
"code": "DATABASE_ERROR",
"message": "Database operation failed"
}
}

Resolution: Retry the request. This is typically a transient error.

Error Handling Best Practices​

1. Always Check Response Status​

const response = await fetch('/api/v1/backups', options);
const data = await response.json();

if (!data.success) {
console.error(`Error ${data.error.code}: ${data.error.message}`);
// Handle specific error codes
switch (data.error.code) {
case 'TOKEN_EXPIRED':
await refreshToken();
break;
case 'RATE_LIMIT_EXCEEDED':
await sleep(data.error.details.retryAfter * 1000);
break;
default:
showErrorToUser(data.error.message);
}
}

2. Implement Retry Logic​

async function fetchWithRetry(url, options, maxRetries = 3) {
for (let i = 0; i < maxRetries; i++) {
try {
const response = await fetch(url, options);
const data = await response.json();

if (data.success) return data;

// Don't retry client errors (4xx)
if (response.status >= 400 && response.status < 500) {
throw new Error(data.error.message);
}

// Retry server errors (5xx)
await sleep(Math.pow(2, i) * 1000); // Exponential backoff
} catch (error) {
if (i === maxRetries - 1) throw error;
}
}
}

3. Handle Rate Limits​

async function handleRateLimit(response) {
if (response.status === 429) {
const retryAfter = response.headers.get('Retry-After') || 60;
console.log(`Rate limited. Retrying in ${retryAfter}s`);
await sleep(retryAfter * 1000);
return true; // Signal to retry
}
return false;
}

4. Log Errors for Debugging​

function logError(error, context) {
console.error({
code: error.code,
message: error.message,
details: error.details,
context,
timestamp: new Date().toISOString()
});
}