Skip to content

Error Handling

Moon ERP uses standard HTTP status codes and returns consistent JSON error responses. This page documents every error format you may encounter.

HTTP Status Codes

CodeMeaningWhen It Occurs
200OKSuccessful read, update, or delete
201CreatedSuccessful resource creation
204No ContentSuccessful operation with no response body
401UnauthenticatedMissing or invalid bearer token
403ForbiddenValid token but insufficient permissions
404Not FoundResource does not exist or has been soft-deleted
422Unprocessable EntityValidation errors in the request body
500Internal Server ErrorUnexpected server-side failure

Error Response Formats

422 -- Validation Error

Returned when the request body fails validation. The errors object contains field names as keys, each mapping to an array of error messages.

json
{
  "message": "The name field is required. (and 1 more error)",
  "errors": {
    "name": [
      "The name field is required."
    ],
    "email": [
      "The email field must be a valid email address."
    ]
  }
}

Multiple errors per field are possible:

json
{
  "message": "The password field must be at least 8 characters. (and 1 more error)",
  "errors": {
    "password": [
      "The password field must be at least 8 characters.",
      "The password field confirmation does not match."
    ]
  }
}

Handling Validation Errors

Iterate over the errors object to display field-level messages in your UI. The top-level message is a summary suitable for a toast notification.

401 -- Unauthenticated

Returned when the request has no Authorization header, the token is missing, or the token has been revoked (e.g., after logout).

json
{
  "message": "Unauthenticated."
}

Common Causes

  • Missing Authorization: Bearer {token} header
  • Token was revoked via the logout endpoint
  • Malformed token string

403 -- Forbidden

Returned when the authenticated user does not have permission to perform the requested action.

json
{
  "message": "This action is unauthorized."
}

Common Causes

  • User's role lacks the required permission
  • Attempting to access a resource belonging to a different company
  • User account is inactive

404 -- Not Found

Returned when the requested resource does not exist or has been soft-deleted.

json
{
  "message": "Resource not found."
}

INFO

Laravel may also return a 404 with a slightly different format for route-model binding failures:

json
{
  "message": "No query results for model [Account] 999."
}

500 -- Internal Server Error

Returned when an unexpected error occurs on the server. In production, the response is intentionally vague to avoid leaking sensitive information.

json
{
  "message": "Server Error"
}

In development environments, the response may include additional debugging information such as a stack trace. This extra detail is never exposed in production.

Error Handling in Code

curl

bash
response=$(curl -s -w "\n%{http_code}" -X POST https://moon-erp.test/api/auth/login \
  -H "Content-Type: application/json" \
  -H "Accept: application/json" \
  -d '{"email": "bad@example.com", "password": "wrong"}')

status=$(echo "$response" | tail -1)
body=$(echo "$response" | sed '$d')

if [ "$status" -ne 200 ]; then
  echo "Error ($status): $body"
fi

Dart

dart
import 'dart:convert';
import 'package:http/http.dart' as http;

final response = await http.post(
  Uri.parse('https://moon-erp.test/api/auth/login'),
  headers: {
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  },
  body: jsonEncode({
    'email': 'bad@example.com',
    'password': 'wrong',
  }),
);

switch (response.statusCode) {
  case 200:
    final data = jsonDecode(response.body);
    // Success -- use data['token']
    break;
  case 401:
    // Invalid credentials or expired token
    print('Unauthenticated: ${response.body}');
    break;
  case 422:
    final errors = jsonDecode(response.body)['errors'] as Map;
    // Display field-level validation errors
    errors.forEach((field, messages) {
      print('$field: ${(messages as List).join(', ')}');
    });
    break;
  case 403:
    // Insufficient permissions
    print('Forbidden: ${response.body}');
    break;
  case 404:
    // Resource not found
    print('Not found: ${response.body}');
    break;
  default:
    // Server error or unexpected status
    print('Error ${response.statusCode}: ${response.body}');
}

Best Practices

  • Always check the HTTP status code before parsing the response body.
  • Display errors field messages next to their corresponding form inputs for 422 responses.
  • On 401 responses, redirect the user to the login screen and clear any stored tokens.
  • Log 500 errors on the client for debugging, but show a generic message to the user.
  • Include the Accept: application/json header on every request to ensure Laravel returns JSON error responses instead of HTML.

Moon ERP API Documentation