Skip to Content
APIError Logging

Error Logging

The Curate-Me API uses a consistent error format across all endpoints. Production errors are tracked in a centralized logging system that supports querying, resolution tracking, and CLI-based management.

Error Response Format

All API errors return a structured JSON response:

{ "error": { "code": "ANALYSIS_FAILED", "message": "The vision agent failed to process the uploaded image.", "details": { "agent": "vision_agent", "reason": "Image resolution too low for analysis", "min_resolution": "256x256" } } }
FieldTypeDescription
error.codestringMachine-readable error code
error.messagestringHuman-readable error description
error.detailsobjectAdditional context (varies by error type)

Error Codes

CodeHTTP StatusDescription
RATE_LIMIT_EXCEEDED429Request rate limit has been exceeded
SUBSCRIPTION_REQUIRED403A paid subscription is required for this feature
INVALID_IMAGE_FORMAT400Uploaded image is not in a supported format (JPEG, PNG, WebP)
IMAGE_TOO_LARGE413Uploaded image exceeds the maximum file size (10 MB)
ANALYSIS_FAILED500An agent in the analysis pipeline encountered an error
SESSION_EXPIRED401The authentication session has expired
INSUFFICIENT_CREDITS403Account does not have enough credits for the requested operation
DUPLICATE_RESOURCE409A resource with the same identifier already exists

HTTP Status Codes

StatusMeaningCommon Causes
400Bad RequestInvalid parameters, malformed JSON, missing required fields
401UnauthorizedMissing or expired token, invalid API key
403ForbiddenInsufficient permissions, subscription required, insufficient credits
404Not FoundResource does not exist or was deleted
409ConflictDuplicate resource creation
413Payload Too LargeFile upload exceeds size limit
429Too Many RequestsRate limit exceeded, includes Retry-After header
500Internal Server ErrorUnexpected server error, agent failure
502Bad GatewayUpstream LLM provider is unavailable
503Service UnavailableScheduled maintenance or temporary overload

Error Log CLI

The platform includes a CLI tool for querying and managing production errors. This requires an ERROR_LOG_API_KEY configured in your environment.

First-Time Setup

./scripts/errors setup # Enter your ERROR_LOG_API_KEY when prompted # Key is stored in ~/.curateme-error-key (not committed to git)

View Recent Errors

./scripts/errors recent

Output:

ID | Time | Code | Message ------------|---------------------|--------------------|--------------------------------- err_abc123 | 2026-02-08 14:23:00 | ANALYSIS_FAILED | Vision agent timeout after 30s err_def456 | 2026-02-08 14:10:00 | RATE_LIMIT_EXCEEDED| Provider rate limit (OpenAI) err_ghi789 | 2026-02-08 13:55:00 | IMAGE_TOO_LARGE | Upload exceeded 10MB limit

Get Error Details

./scripts/errors get err_abc123

Returns the full error record including stack trace, request context, and agent execution state.

Get Error Statistics

./scripts/errors summary

Output:

Error Summary (last 24h) ------------------------ Total errors: 47 Unresolved: 12 Top error codes: ANALYSIS_FAILED: 18 (38%) RATE_LIMIT_EXCEEDED: 15 (32%) IMAGE_TOO_LARGE: 8 (17%) SESSION_EXPIRED: 6 (13%)

Resolve an Error

After fixing the root cause and deploying, mark the error as resolved:

./scripts/errors resolve err_abc123 "Fixed timeout handling in vision agent (PR #142)"

Error Handling Best Practices

When integrating with the API, implement error handling that accounts for the following:

  1. Retry on 429 and 503 — Use the Retry-After header value for backoff timing.
  2. Do not retry on 400 or 401 — These indicate client-side issues that require changes to the request.
  3. Handle SSE errors — During streaming responses, listen for error events and handle them gracefully.
  4. Log the error code — Use the error.code field for programmatic error handling rather than parsing the message string.
try { const response = await fetch('/api/v1/analyze', { method: 'POST', ... }); if (!response.ok) { const { error } = await response.json(); switch (error.code) { case 'RATE_LIMIT_EXCEEDED': const retryAfter = response.headers.get('Retry-After'); await delay(retryAfter * 1000); return retry(request); case 'SESSION_EXPIRED': await refreshToken(); return retry(request); default: throw new ApiError(error.code, error.message); } } } catch (err) { // Network or unexpected errors console.error('API request failed:', err); }