Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.law4devs.eu/llms.txt

Use this file to discover all available pages before exploring further.

Overview

All SDK errors inherit from Law4DevsError. Each exception class maps to a specific HTTP status code or error condition. The SDK automatically retries transient errors (429 and 5xx) with exponential backoff before raising.

Exception Hierarchy

Law4DevsError
├── NotFoundError       (HTTP 404)
├── ValidationError     (HTTP 400 / 422)
├── RateLimitError      (HTTP 429)
└── ServerError         (HTTP 500 / 502 / 503 / 504)
All exceptions expose a status_code attribute containing the underlying HTTP status code.
from law4devs.exceptions import (
    Law4DevsError,
    NotFoundError,
    ValidationError,
    RateLimitError,
    ServerError,
)

Exception Reference

Law4DevsError

Base class for all SDK exceptions. Catch this to handle any API error.
AttributeTypeDescription
status_codeintHTTP status code
messagestrHuman-readable error message

NotFoundError

Raised when the requested resource does not exist (HTTP 404).
try:
    article = client.articles.get("gdpr", 9999)
except NotFoundError as e:
    print(f"Article not found: {e.message}")

ValidationError

Raised when the request parameters are invalid (HTTP 400 or 422).
try:
    page = client.articles.list("gdpr", per_page=999)  # exceeds max
except ValidationError as e:
    print(f"Bad request: {e.message}")

RateLimitError

Raised when the rate limit is exceeded and all retries are exhausted (HTTP 429).
try:
    results = client.search.query("encryption")
except RateLimitError as e:
    print(f"Rate limited. Retry after: {e.retry_after} seconds")

ServerError

Raised when the server returns a 5xx response and all retries are exhausted.
try:
    page = client.frameworks.list()
except ServerError as e:
    print(f"Server error {e.status_code}: {e.message}")

Auto-Retry Behaviour

The SDK automatically retries failed requests on HTTP 429 and 5xx responses. Retries use exponential backoff with jitter.
  • Default retries: 3
  • Backoff: 1s, 2s, 4s (with jitter)
  • Retried status codes: 429, 500, 502, 503, 504
If all retries are exhausted, the appropriate exception (RateLimitError or ServerError) is raised. Configure the retry count at construction time:
from law4devs import Law4DevsClient

# Disable retries
client = Law4DevsClient(max_retries=0)

# More retries for a long-running batch job
client = Law4DevsClient(max_retries=5)

Practical Patterns

Handle a missing resource

from law4devs import Law4DevsClient
from law4devs.exceptions import NotFoundError

client = Law4DevsClient()

try:
    article = client.articles.get("gdpr", 999)
except NotFoundError:
    print("That article doesn't exist")

safe_get helper

A utility that returns None instead of raising on 404:
from law4devs.exceptions import NotFoundError

def safe_get_article(client, framework_slug, article_number):
    try:
        return client.articles.get(framework_slug, article_number)
    except NotFoundError:
        return None

article = safe_get_article(client, "gdpr", 999)
if article is None:
    print("Not found")
else:
    print(article.title)

Catch all API errors

from law4devs.exceptions import Law4DevsError

try:
    page = client.frameworks.list()
except Law4DevsError as e:
    print(f"API error {e.status_code}: {e.message}")

Handle rate limits explicitly

import time
from law4devs.exceptions import RateLimitError

def fetch_with_backoff(client, framework_slug):
    for attempt in range(5):
        try:
            return list(client.articles.iter(framework_slug))
        except RateLimitError as e:
            wait = getattr(e, "retry_after", 2 ** attempt)
            print(f"Rate limited. Waiting {wait}s (attempt {attempt + 1}/5)")
            time.sleep(wait)
    raise RuntimeError("Exhausted retries")

Distinguish error types

from law4devs.exceptions import (
    NotFoundError,
    ValidationError,
    RateLimitError,
    ServerError,
    Law4DevsError,
)

try:
    result = client.articles.get("gdpr", article_number)
except NotFoundError:
    print("Article not found — check the article number")
except ValidationError as e:
    print(f"Invalid parameters: {e.message}")
except RateLimitError:
    print("Too many requests — slow down or add an API key")
except ServerError as e:
    print(f"Server problem ({e.status_code}) — try again later")
except Law4DevsError as e:
    print(f"Unexpected error {e.status_code}: {e.message}")

Validate a framework slug before use

from law4devs.exceptions import NotFoundError

def framework_exists(client, slug):
    try:
        client.frameworks.get(slug)
        return True
    except NotFoundError:
        return False

if not framework_exists(client, "unknown-framework"):
    print("Framework not found")

Timeout Errors

Network timeouts raise Python’s built-in TimeoutError (not a Law4DevsError). Configure the timeout per client:
# Allow up to 60 seconds per request
client = Law4DevsClient(timeout=60)
try:
    page = client.frameworks.list()
except TimeoutError:
    print("Request timed out — the server may be slow")
For batch jobs that process thousands of items, set max_retries=5 and a generous timeout=60. The SDK will handle intermittent failures automatically.
404 responses are not retried automatically — they indicate a resource genuinely does not exist, not a transient failure.