Skip to main content
The BookMe API uses standard HTTP status codes and returns detailed JSON error responses to help you understand and resolve issues quickly.

Error Response Format

Standard Error Response

All errors return a JSON object with an error field:
{
  "error": "Error message describing what went wrong"
}
Source: internal/handler/response.go:14-17

Validation Error Response

Validation errors include additional details field with field-specific error messages:
{
  "error": "Validation failed",
  "details": {
    "startTime": "cannot be in the past",
    "endTime": "must be after start time"
  }
}
Source: internal/handler/response.go:14-17, internal/handler/response.go:28-34

HTTP Status Codes

The API uses standard HTTP status codes to indicate success or failure:
Status CodeMeaningWhen It Occurs
200 OKSuccessRequest completed successfully
204 No ContentSuccess (no body)Successful deletion
400 Bad RequestClient errorInvalid request data or validation failure
401 UnauthorizedAuthentication errorMissing or invalid JWT token
403 ForbiddenAuthorization errorValid token but insufficient permissions
404 Not FoundResource not foundRequested resource doesn’t exist
409 ConflictResource conflictTime slot already booked
429 Too Many RequestsRate limit exceededToo many requests from your IP
500 Internal Server ErrorServer errorUnexpected server-side error
504 Gateway TimeoutTimeoutRequest to external service timed out

Error Categories

Validation Errors (400)

Validation errors occur when request data doesn’t meet the required format or business rules.
{
  "error": "Validation failed",
  "details": {
    "roomId": "is required",
    "startTime": "must be a valid ISO 8601 timestamp",
    "endTime": "must be after start time"
  }
}
Common Validation Errors:
  • Missing required fields (roomId, startTime, endTime)
  • Invalid timestamp format (must be ISO 8601)
  • Start time in the past
  • End time before start time
  • Booking outside school hours (6:00 AM - 8:00 PM)
  • Duration exceeds maximum (4 hours for students)
  • Date range exceeds 60 days
Source: internal/handler/response.go:57-61
Validation errors always return 400 Bad Request with field-specific error details.

Authentication Errors (401)

Authentication errors occur when JWT token is missing, invalid, or expired.
Error MessageCauseSolution
no auth header included in requestMissing Authorization headerInclude Authorization: Bearer <token> header
bearer token is emptyEmpty token after “Bearer “Provide valid JWT token
bearer token is incorrectMissing “Bearer ” prefixUse format Bearer <token>
invalid tokenToken signature validation failedRe-authenticate to get new token
expired tokenToken older than 1 hourRe-authenticate to get new token
Source: internal/auth/auth.go:49-55
{
  "error": "expired token"
}
JWT tokens expire after 1 hour. Implement token refresh logic in your application.

Authorization Errors (403)

Authorization errors occur when authenticated user lacks permission for the requested action.

Service Authorization Errors

Error MessageStatus CodeWhen It Occurs
you are not authorized to perform this action403Generic authorization failure
unauthorized to cancel this reservation403Non-owner/non-staff trying to cancel
Source: internal/service/errors.go:40-43, internal/service/errors.go:60-63

OAuth Authorization Errors

Error MessageStatus CodeWhen It Occurs
oauth state mismatch403CSRF protection - state token invalid
access denied: only helsinki campus student allowed403User not from Hive Helsinki (campus_id ≠ 13)
invalid or missing state403OAuth state validation failed
Source: internal/oauth/errors.go:41-44, internal/oauth/errors.go:61-64, internal/oauth/errors.go:77-80
Campus Restriction: Only users from Hive Helsinki (42 campus ID 13) can access the API.

Not Found Errors (404)

Returned when requested resource doesn’t exist.
Error MessageWhat Doesn’t Exist
reservation not foundReservation with given ID
room not foundRoom with given ID
Source: internal/service/errors.go:36-39, internal/service/errors.go:44-47
curl -X DELETE http://localhost:8080/api/v1/reservations/999 \
  -H "Authorization: Bearer YOUR_TOKEN"

Conflict Errors (409)

Returned when request conflicts with current state.
Error MessageCauseSolution
this time slot is already bookedOverlapping reservation existsChoose different time slot
Source: internal/service/errors.go:48-51
curl -X POST http://localhost:8080/api/v1/reservations \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "roomId": 1,
    "startTime": "2025-01-28T14:00:00Z",
    "endTime": "2025-01-28T16:00:00Z"
  }'
Use GET /api/v1/reservations to check available time slots before creating a reservation.

Bad Request Errors (400)

Business logic violations that don’t involve validation.
Error MessageCause
reservation exceeds maximum allowed durationStudent booking > 4 hours
invalid or missing oauth codeOAuth authorization code missing
Source: internal/service/errors.go:52-55, internal/oauth/errors.go:53-56
Maximum Duration Rules:
  • Students: 4 hours maximum
  • Staff: Unlimited

Server Errors (500)

Internal server errors are logged and return generic error messages to clients.
Error MessageInternal Cause
Internal server errorDatabase error, service failure
failed to get UserDatabase query failed
failed to fetch reservationsDatabase query failed
oauth token exchange failedFailed to exchange code for token
failed to fetch user info from oauth providerFailed to get user data from 42
failed to find or create userDatabase error during user creation
failed to get sessionSession storage error
failed to save sessionSession save error
Source: internal/handler/response.go:63-75, internal/service/errors.go:32-35, internal/service/errors.go:56-59, internal/oauth/errors.go:45-76
Server errors (5xx) are logged on the server side with full error details. The client receives a generic “Internal server error” message to avoid exposing sensitive information.

Timeout Errors (504)

Error MessageCause
oauth request timeoutRequest to 42 Intra API exceeded 15 seconds
Source: internal/oauth/errors.go:57-60

Error Handling Flow

The API handles errors in a centralized manner:
// Error handling priority
1. Validation errors (400) → Return with field details
2. Service errors (4xx/5xx) → Log 5xx, return message
3. OAuth errors (4xx/5xx) → Log 5xx, return message
4. Unknown errors (500) → Log error, return generic message
Source: internal/handler/response.go:53-93
1

Validation Check

First, check if error is a ValidationError
  • Return 400 Bad Request with field details
  • No server-side logging (expected errors)
2

Service Error Check

Check if error is a ServiceError
  • If status ≥ 500: Log error and return “Internal server error”
  • Otherwise: Return error message with status code
3

OAuth Error Check

Check if error is an OauthError
  • If status ≥ 500: Log error and return “Internal server error”
  • Otherwise: Return error message with status code
4

Unexpected Error

Any other error type
  • Log error as “unexpected error”
  • Return 500 Internal Server Error

Best Practices

Client-Side Error Handling

async function createReservation(token, data) {
  try {
    const response = await fetch(
      'http://localhost:8080/api/v1/reservations',
      {
        method: 'POST',
        headers: {
          'Authorization': `Bearer ${token}`,
          'Content-Type': 'application/json'
        },
        body: JSON.stringify(data)
      }
    );
    
    if (!response.ok) {
      const error = await response.json();
      
      // Handle specific error types
      switch (response.status) {
        case 400:
          // Validation error - show field-specific errors
          if (error.details) {
            Object.keys(error.details).forEach(field => {
              console.error(`${field}: ${error.details[field]}`);
            });
          }
          break;
          
        case 401:
          // Token expired - redirect to login
          window.location.href = '/oauth/login';
          break;
          
        case 403:
          // Not authorized
          alert('You do not have permission to perform this action');
          break;
          
        case 404:
          // Resource not found
          alert('Resource not found');
          break;
          
        case 409:
          // Conflict - time slot taken
          alert('This time slot is already booked');
          break;
          
        case 429:
          // Rate limited - wait and retry
          const retryAfter = response.headers.get('Retry-After');
          await new Promise(r => setTimeout(r, retryAfter * 1000));
          return createReservation(token, data); // Retry
          
        case 500:
        case 504:
          // Server error - show generic message
          alert('Server error. Please try again later.');
          break;
      }
      
      throw new Error(error.error);
    }
    
    return await response.json();
    
  } catch (err) {
    console.error('Request failed:', err);
    throw err;
  }
}

Logging and Debugging

The server logs all 5xx errors with full details. Check server logs for debugging internal errors:
ERROR service error error={...}
ERROR oauth error error={...}
ERROR unexpected error error={...}

Rate Limit Handling

Always check for 429 status and respect the Retry-After header:
if (response.status === 429) {
  const retryAfter = parseInt(response.headers.get('Retry-After'));
  await sleep(retryAfter * 1000);
  // Retry request
}
See Rate Limiting for detailed information.

Error Reference

Quick Reference Table

StatusError TypeLogged?Details Field?
400ValidationNoYes
400Bad RequestNoNo
401AuthenticationNoNo
403AuthorizationNoNo
404Not FoundNoNo
409ConflictNoNo
429Rate LimitYes (warn)No
500Server ErrorYes (error)No
504TimeoutNoNo
All error responses include the Content-Type: application/json header except for rate limit errors which may return plain text.

Build docs developers (and LLMs) love