RealWorld API Error Handling Best Practices: A Complete Implementation Guide

The RealWorld specification enforces a unified error contract using the GenericErrorModel schema with consistent HTTP status codes, frontend .error-messages rendering, and Playwright test verification to ensure reliable cross-platform failure communication.

The gothinkster/realworld repository defines a strict, implementation-agnostic protocol for error handling within the RealWorld API that governs how backends serialize failures and how frontends consume them. By adhering to these standards—documented in the OpenAPI specification and enforced via end-to-end tests—developers ensure that validation errors, authentication failures, and server exceptions are communicated predictably across all technology stacks.

Unified Error Response Format

All error responses must conform to the GenericErrorModel schema defined in specs/api/openapi.yml. This schema requires a JSON object containing an errors field, where keys identify the failing entity (such as email, title, or token) and values are arrays of human-readable strings.

{
  "errors": {
    "title": ["can't be empty"],
    "body": ["can't be blank"]
  }
}

This structure appears in the OpenAPI specification at components.schemas.GenericErrorModel (lines 33-44) and is referenced by the GenericError response definition (lines 63-74). The contract ensures that clients can programmatically parse field-specific validation failures without inspecting proprietary error formats.

Standard HTTP Status Codes

The RealWorld API assigns specific semantic meanings to HTTP status codes, documented in apps/documentation/src/content/docs/specifications/backend/error-handling.md (lines 5-25). Implementations must return the following codes under the specified conditions:

  • 401 Unauthorized – The request lacks a valid JWT token or the provided token has expired.
  • 403 Forbidden – The user is authenticated but lacks permission to access the requested resource.
  • 404 Not Found – The requested resource (e.g., an article or profile) does not exist.
  • 422 Unprocessable Entity – The request body fails schema validation, such as empty required fields or malformed email addresses.
  • 5xx Server Error – An unexpected internal error occurred; clients should implement retry logic or display a generic connection failure message.

Each endpoint in the OpenAPI specification explicitly declares which error codes it may return, allowing client generators to create type-safe error handling logic.

Frontend Error Consumption Patterns

Client applications must follow a four-step pattern to display errors according to the RealWorld UI specification:

  1. Detect non-2xx status codes using the response object (e.g., !response.ok in Fetch API).
  2. Parse the JSON payload and extract the errors object.
  3. Flatten message arrays into a single list of strings suitable for UI rendering.
  4. Render the list inside a <ul class="error-messages"> element as defined in apps/documentation/src/content/docs/specifications/frontend/templates.md (lines 235-242).

The CSS selector .error-messages is styled in assets/theme/styles.css (line 1111) to ensure consistent visual presentation across all implementations. This markup contract allows the automated test suite to verify that errors are actually visible to users.

Backend Implementation Guidelines

When handling failures, backend implementations must:

  • Return 422 for validation errors and 5xx for unexpected internal exceptions.
  • Serialize the response using the GenericErrorModel schema without exposing stack traces or sensitive system details.
  • Ensure that the errors object keys map directly to input field names for automatic form association.

For example, in a Node.js/Express controller:

app.post('/api/articles', async (req, res) => {
  const validationErrors = validateArticle(req.body.article);
  if (validationErrors) {
    return res.status(422).json({ errors: validationErrors });
  }
  // Process valid request...
});

This pattern aligns with the OpenAPI-defined 422 response for the POST /articles endpoint in specs/api/openapi.yml.

Automated Testing Requirements

The Playwright test suite in specs/e2e/error-handling.spec.ts (lines 40-58) enforces the error handling contract by verifying that failed API calls result in visible error messages. The helper utilities in specs/e2e/helpers/auth.ts (lines 12-21) demonstrate the recommended client-side error extraction pattern:

async function handleApiError(resp: Response) {
  if (resp.ok) return await resp.json();
  
  const { errors } = await resp.json() as { errors: Record<string, string[]> };
  const messages = Object.values(errors).flat();
  throw new Error(messages.join(' '));
}

End-to-end tests specifically check for the presence of the .error-messages CSS class and validate that it contains the expected error text, ensuring that implementations honor both the API contract and the UI rendering requirements.

Summary

  • RealWorld API error handling relies on the GenericErrorModel schema with an errors object mapping field names to message arrays.
  • HTTP status codes follow strict semantics: 401 for authentication, 403 for authorization, 404 for missing resources, 422 for validation, and 5xx for server failures.
  • Frontend implementations must render errors within a <ul class="error-messages"> element to satisfy the specification and automated test suite.
  • Source files defining these standards include specs/api/openapi.yml, apps/documentation/src/content/docs/specifications/backend/error-handling.md, and specs/e2e/error-handling.spec.ts.
  • Security best practices prohibit exposing stack traces or internal system details in error responses.

Frequently Asked Questions

What JSON structure must RealWorld API error responses follow?

Error responses must follow the GenericErrorModel schema defined in specs/api/openapi.yml, returning an object with an errors key containing field-specific message arrays, such as {"errors": {"email": ["is invalid"]}}.

Which HTTP status code should I return for validation failures?

Return 422 Unprocessable Entity for any request that fails schema validation (e.g., missing required fields or invalid formats), as documented in the backend error-handling specification and the OpenAPI contract.

How does the frontend know where to display API errors?

The specification requires rendering error lists inside a <ul> element with the CSS class error-messages, styled by assets/theme/styles.css and verified by the Playwright tests in specs/e2e/error-handling.spec.ts.

Can I include stack traces in error responses for debugging?

No. The RealWorld API security guidelines explicitly prohibit exposing stack traces or sensitive internal details. Only user-readable error messages should be serialized using the GenericErrorModel schema.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →