Skip to main content

Overview

The Hub API uses RFC 7807 Problem Details for error responses. All errors return a consistent JSON structure with detailed information about what went wrong.

Error Response Format

Every error response follows this structure:
{
  "type": "/errors/validation-error",
  "title": "Validation Error",
  "status": 400,
  "detail": "Request validation failed",
  "instance": "/api/bookings",
  "timestamp": "2026-03-08T10:30:00Z",
  "errors": {
    "resourceId": "must not be null",
    "startTime": "must be in the future"
  }
}
type
string
required
URI reference identifying the problem type (e.g., /errors/not-found)
title
string
required
Short, human-readable summary of the problem type
status
integer
required
HTTP status code (400, 401, 403, 404, 409, 422, 500)
detail
string
required
Human-readable explanation specific to this occurrence
instance
string
required
URI reference identifying the specific occurrence (the request path)
timestamp
string
required
ISO 8601 timestamp when the error occurred
errors
object
Additional context about validation errors (field-specific messages)
traceId
string
Distributed trace ID (only included when app.expose-trace-id=true)

HTTP Status Codes

The API uses standard HTTP status codes:

400 Bad Request

The request is malformed or contains invalid data. Common causes:
  • Invalid JSON syntax
  • Missing required fields
  • Type mismatches
  • Constraint violations
{
  "type": "/errors/validation-error",
  "title": "Validation Error",
  "status": 400,
  "detail": "Request validation failed",
  "instance": "/api/owner/venues",
  "timestamp": "2026-03-08T10:30:00Z",
  "errors": {
    "name": "must not be blank",
    "latitude": "must be between -90 and 90",
    "longitude": "must be between -180 and 180"
  }
}

401 Unauthorized

Authentication is required or the provided token is invalid.
{
  "type": "/errors/unauthorized",
  "title": "Unauthorized",
  "status": 401,
  "detail": "Authentication is required to access this resource",
  "instance": "/api/bookings/my",
  "timestamp": "2026-03-08T10:30:00Z"
}
How to fix:
  • Include valid Authorization: Bearer <token> header
  • Refresh expired access token
  • Verify token audience and issuer

403 Forbidden

Authentication succeeded but the user lacks permission.
{
  "type": "/errors/forbidden",
  "title": "Forbidden",
  "status": 403,
  "detail": "You don't have permission to access this resource",
  "instance": "/api/owner/venues/123e4567-e89b-12d3-a456-426614174000",
  "timestamp": "2026-03-08T10:30:00Z"
}
Common scenarios:
  • Accessing another user’s resources
  • Attempting owner actions without venue ownership
  • Accessing admin endpoints without admin role
403 Forbidden differs from 401 Unauthorized. With 403, you are authenticated but not authorized.

404 Not Found

The requested resource does not exist.
{
  "type": "/errors/not-found",
  "title": "Not Found",
  "status": 404,
  "detail": "Venue not found with id: 123e4567-e89b-12d3-a456-426614174000",
  "instance": "/api/venues/123e4567-e89b-12d3-a456-426614174000",
  "timestamp": "2026-03-08T10:30:00Z"
}

409 Conflict

The request conflicts with the current state of the server.
{
  "type": "/errors/conflict",
  "title": "Conflict",
  "status": 409,
  "detail": "The selected time slot is no longer available",
  "instance": "/api/bookings",
  "timestamp": "2026-03-08T10:30:00Z"
}

422 Unprocessable Entity

The request is well-formed but cannot be processed due to business logic rules.
{
  "type": "/errors/unprocessable",
  "title": "Unprocessable",
  "status": 422,
  "detail": "Booking cannot be cancelled less than 24 hours before start time",
  "instance": "/api/bookings/123/cancel",
  "timestamp": "2026-03-08T10:30:00Z"
}

500 Internal Server Error

An unexpected error occurred on the server.
{
  "type": "/errors/internal-server-error",
  "title": "Internal Server Error",
  "status": 500,
  "detail": "Unexpected error occurred",
  "instance": "/api/bookings",
  "timestamp": "2026-03-08T10:30:00Z",
  "traceId": "a1b2c3d4-e5f6-4a5b-8c9d-0e1f2a3b4c5d"
}
When app.expose-trace-id=true, internal errors include a traceId for debugging. Provide this ID when contacting support.

Error Types Reference

Complete list of error types returned by the API:
Error TypeStatusDescription
/errors/unauthorized401Authentication required or token invalid
/errors/forbidden403Insufficient permissions
/errors/validation-error400Request validation failed
/errors/malformed-json400Invalid JSON in request body
/errors/bad-request400Invalid request
/errors/missing-parameter400Required parameter missing
/errors/not-found404Resource not found
/errors/conflict409Resource conflict or constraint violation
/errors/unprocessable422Business logic rule violation
/errors/internal-server-error500Unexpected server error

Domain-Specific Errors

The API includes domain-specific error handling for different modules:

Booking Errors

  • BookingNotFoundException - 404
  • SlotNotAvailableException - 409
  • BookingCancellationNotAllowedException - 422

Match Errors

  • MatchNotFoundException - 404
  • InvitationNotFoundException - 404
  • MatchFullException - 422
  • TeamFullException - 422
  • PlayerAlreadyJoinedException - 422
  • PlayerTimeConflictException - 422
  • MatchNotOpenException - 422
  • MatchLeaveNotAllowedException - 422
  • PlayerMatchBannedException - 422
  • TooManyActiveMatchesException - 422
  • MatchCreationCooldownException - 422
  • InvitationAlreadyRespondedException - 422
  • InvitationNotYoursException - 403
  • NotMatchOrganizerException - 403

Venue Errors

  • VenueNotFoundException - 404
  • VenueImageNotFoundException - 404

Resource Errors

  • ResourceNotFoundException - 404
  • ResourceImageNotFoundException - 404

User Errors

  • UserProfileNotFoundException - 404

Best Practices

Handle Errors Gracefully

Always check the HTTP status code and parse the error response:
try {
  const response = await fetch('/api/bookings', {
    method: 'POST',
    headers: {
      'Authorization': `Bearer ${token}`,
      'Content-Type': 'application/json'
    },
    body: JSON.stringify(bookingData)
  });

  if (!response.ok) {
    const error = await response.json();
    
    // Handle specific error types
    switch (error.type) {
      case '/errors/validation-error':
        // Show field-specific validation errors
        displayValidationErrors(error.errors);
        break;
      case '/errors/conflict':
        // Slot not available - suggest alternatives
        suggestAlternativeSlots();
        break;
      case '/errors/unauthorized':
        // Refresh token and retry
        await refreshToken();
        break;
      default:
        // Generic error handling
        showError(error.detail);
    }
    
    return;
  }

  const booking = await response.json();
  // Success handling...
} catch (error) {
  // Network or parsing error
  console.error('Request failed:', error);
}

Use Validation Errors

For 400 validation errors, the errors object maps field names to error messages:
{
  "errors": {
    "name": "must not be blank",
    "latitude": "must be between -90 and 90"
  }
}
Display these errors next to the corresponding form fields.

Log Trace IDs

For 500 errors, save the traceId for support requests:
if (error.status === 500 && error.traceId) {
  console.error(`Server error - Trace ID: ${error.traceId}`);
  // Show trace ID to user for support
}
Never expose sensitive information in error messages. The API follows this principle - internal errors return generic messages.

Testing Error Scenarios

Test common error scenarios:
curl -X GET https://api.yourdomain.com/api/bookings/my
# Returns 401 Unauthorized

Next Steps

Authentication

Learn about Auth0 OAuth2 authentication

API Endpoints

Explore available API endpoints

Build docs developers (and LLMs) love