Skip to content

Error Handling

Raise Exceptions

Raise GeminiException subclasses to return status codes:

from xitzin import NotFound, BadRequest

@app.gemini("/user/{user_id}")
def get_user(request: Request, user_id: int):
    user = database.get(user_id)
    if not user:
        raise NotFound(f"User {user_id} not found")
    return f"# {user.name}"

@app.gemini("/validate")
def validate(request: Request):
    if not request.query:
        raise BadRequest("Query parameter required")
    return f"Valid: {request.query}"

Available Exceptions

Input Required (1x)

from xitzin import InputRequired, SensitiveInputRequired

raise InputRequired("Enter your name")  # Status 10
raise SensitiveInputRequired("Enter password")  # Status 11

Temporary Failures (4x)

from xitzin import (
    TemporaryFailure,
    ServerUnavailable,
    CGIError,
    ProxyError,
    SlowDown,
)

raise TemporaryFailure("Try again later")      # Status 40
raise ServerUnavailable("Maintenance mode")     # Status 41
raise CGIError("Script failed")                 # Status 42
raise ProxyError("Upstream failed")             # Status 43
raise SlowDown("Rate limited")                  # Status 44

Permanent Failures (5x)

from xitzin import (
    PermanentFailure,
    NotFound,
    Gone,
    ProxyRequestRefused,
    BadRequest,
)

raise PermanentFailure("Cannot process")        # Status 50
raise NotFound("Page not found")                # Status 51
raise Gone("Content removed")                   # Status 52
raise ProxyRequestRefused("Blocked")            # Status 53
raise BadRequest("Invalid input")               # Status 59

Certificate Errors (6x)

from xitzin import (
    CertificateRequired,
    CertificateNotAuthorized,
    CertificateNotValid,
)

raise CertificateRequired("Login required")     # Status 60
raise CertificateNotAuthorized("Not allowed")   # Status 61
raise CertificateNotValid("Cert expired")       # Status 62

Custom Error Messages

Each exception accepts an optional message:

# With custom message
raise NotFound("Article 123 does not exist")

# With default message
raise NotFound()  # Uses "Not found"

Status Code Reference

Status Exception Meaning
10 InputRequired Input requested
11 SensitiveInputRequired Sensitive input requested
40 TemporaryFailure Temporary failure
41 ServerUnavailable Server unavailable
42 CGIError CGI error
43 ProxyError Proxy error
44 SlowDown Rate limited
50 PermanentFailure Permanent failure
51 NotFound Not found
52 Gone Gone
53 ProxyRequestRefused Proxy refused
59 BadRequest Bad request
60 CertificateRequired Certificate required
61 CertificateNotAuthorized Not authorized
62 CertificateNotValid Invalid certificate

Handle Errors in Middleware

Catch and handle errors globally:

from nauyaca import GeminiResponse, StatusCode

@app.middleware
async def error_handler(request: Request, call_next):
    try:
        return await call_next(request)
    except GeminiException as e:
        # Let Xitzin handle GeminiExceptions normally
        raise
    except Exception as e:
        # Log unexpected errors
        print(f"Unexpected error: {e}")
        return GeminiResponse(
            status=StatusCode.TEMPORARY_FAILURE,
            meta="An unexpected error occurred"
        )

Custom Exception Classes

Create domain-specific exceptions:

class ArticleNotFound(NotFound):
    def __init__(self, article_id: int):
        super().__init__(f"Article {article_id} not found")
        self.article_id = article_id

@app.gemini("/article/{article_id}")
def get_article(request: Request, article_id: int):
    article = articles.get(article_id)
    if not article:
        raise ArticleNotFound(article_id)
    return f"# {article.title}"