# Error Handling The Dinie API uses the **RFC 9457 Problem Details** format for all error responses. Every error has a consistent structure, a machine-readable type URI, and a human-readable description. ## Error Response Format All errors are returned with content type `application/problem+json`: ```json { "type": "https://api.dinie.com.br/errors/invalid-request", "title": "Invalid Request", "status": 400, "detail": "The 'cpf' field is required.", "instance": "/v3/customers" } ``` ### Core Fields | Field | Type | Description | | --- | --- | --- | | `type` | string (URI) | URI that identifies the error type. Resolves to the documentation. | | `title` | string | Short summary of the problem type. Consistent across all occurrences. | | `status` | integer | HTTP status code for this occurrence. | | `detail` | string | Human-readable explanation specific to this occurrence. | | `instance` | string (URI) | URI that identifies this specific occurrence (usually the request path). | ### Extension Fields | Field | Type | Description | | --- | --- | --- | | `code` | string | Machine-readable sub-type (e.g., `missing_required_field`, `invalid_cpf`) | | `param` | string | The request parameter that caused the error | | `errors` | array | List of individual per-field errors (for validation failures) | > **Tip:** Use the `code` field for programmatic error handling. The `type` identifies the broad category, while the `code` pinpoints the specific problem. ## Field Validation Errors When a request has correct syntax but invalid field values, the response includes an `errors` array with details for each invalid field: ```json { "type": "https://api.dinie.com.br/errors/validation-failed", "title": "Validation Failed", "status": 422, "detail": "One or more fields failed validation.", "errors": [ { "param": "email", "detail": "Must be a valid email address.", "code": "invalid_format" }, { "param": "cpf", "detail": "CPF is already registered.", "code": "already_exists" } ] } ``` Each item in the `errors` array contains: | Field | Description | | --- | --- | | `param` | The field name that failed validation | | `detail` | Human-readable explanation of the validation failure | | `code` | Machine-readable validation error code | ## Error Type Reference | Status | Type | Title | When | | --- | --- | --- | --- | | 400 | [`invalid-request`](/errors/invalid-request) | Invalid Request | Malformed syntax, missing content type, or invalid parameters | | 401 | [`authentication-failed`](/errors/authentication-failed) | Authentication Failed | Missing or invalid credentials or token | | 403 | [`forbidden`](/errors/forbidden) | Forbidden | Valid token but insufficient permissions | | 404 | [`not-found`](/errors/not-found) | Not Found | Resource does not exist or is not accessible for this partner | | 409 | [`conflict`](/errors/conflict) | Conflict | Resource already exists or action conflicts with current state | | 422 | [`validation-failed`](/errors/validation-failed) | Validation Failed | Request parsed successfully but field values are invalid | | 429 | [`rate-limit-exceeded`](/errors/rate-limit-exceeded) | Rate Limit Exceeded | Too many requests. Check the `Retry-After` header. | | 500 | [`internal`](/errors/internal) | Internal Error | Unexpected server error | All type URIs are prefixed with `https://api.dinie.com.br`. Click the type to see common causes, examples, and resolution. ## SDK Error Handling The SDKs throw typed exceptions for each error category: ```typescript Node.js import Dinie, { AuthenticationError, NotFoundError, ValidationError, RateLimitError, } from "dinie"; try { const customer = await dinie.customers.create({ cpf: "invalid", name: "Joao Silva", email: "joao@example.com", }); } catch (error) { if (error instanceof ValidationError) { console.error("Validation failed:", error.message); for (const fieldError of error.errors) { console.error(` ${fieldError.param}: ${fieldError.detail}`); } } else if (error instanceof AuthenticationError) { console.error("Authentication failed -- check your credentials"); } else if (error instanceof RateLimitError) { console.error(`Rate limit reached. Retry in ${error.retryAfter}s`); } else if (error instanceof NotFoundError) { console.error("Resource not found"); } } ``` ```ruby Ruby require "dinie" begin customer = dinie.customers.create( cpf: "invalid", name: "Joao Silva", email: "joao@example.com" ) rescue Dinie::ValidationError => e puts "Validation failed: #{e.message}" e.errors.each do |field_error| puts " #{field_error.param}: #{field_error.detail}" end rescue Dinie::AuthenticationError puts "Authentication failed -- check your credentials" rescue Dinie::RateLimitError => e puts "Rate limit reached. Retry in #{e.retry_after}s" rescue Dinie::NotFoundError puts "Resource not found" end ``` ```python Python from dinie import ( AuthenticationError, NotFoundError, ValidationError, RateLimitError, ) try: customer = dinie.customers.create( cpf="invalid", name="Joao Silva", email="joao@example.com", ) except ValidationError as e: print(f"Validation failed: {e.message}") for field_error in e.errors: print(f" {field_error.param}: {field_error.detail}") except AuthenticationError: print("Authentication failed -- check your credentials") except RateLimitError as e: print(f"Rate limit reached. Retry in {e.retry_after}s") except NotFoundError: print("Resource not found") ``` ## Rate Limiting All responses include rate limit headers: ```http X-RateLimit-Limit: 100 X-RateLimit-Remaining: 97 X-RateLimit-Reset: 1709478060 ``` When rate limited (429), the `Retry-After` header indicates how many seconds to wait: ```http Retry-After: 30 ``` ## Retry Strategy Not all errors should be retried. Use this table to decide: | Status | Retry? | Strategy | | --- | --- | --- | | 400 | No | Fix the request. The payload is malformed. | | 401 | Once | Renew the access token and resend. | | 403 | No | Check permissions with your account manager. | | 404 | No | The resource does not exist. | | 409 | No | Resolve the conflict (e.g., duplicate resource). | | 422 | No | Fix the invalid field values. | | 429 | Yes | Wait for the time indicated in `Retry-After` and resend. | | 500 | Yes | Retry with exponential backoff. | ### Exponential Backoff For retryable errors (429, 500), use exponential backoff with jitter: ```typescript const MAX_RETRIES = 3; async function requestWithRetry(fn: () => Promise) { for (let attempt = 0; attempt <= MAX_RETRIES; attempt++) { try { return await fn(); } catch (error) { if (!isRetryable(error) || attempt === MAX_RETRIES) throw error; const baseDelay = Math.pow(2, attempt) * 1000; // 1s, 2s, 4s const jitter = Math.random() * 500; await sleep(baseDelay + jitter); } } } ``` > **Info:** The SDKs implement automatic retries with exponential backoff for `429` and `500` responses. You can configure the maximum number of retries when initializing the client.