Skip to content

Error Handling

The SDK raises four kinds of exceptions, each with a distinct meaning. Handle them explicitly — generic except: blocks throw away the information you need to recover.

The exception map

ExceptionWhenRecovery
PromptNotReleasedInEnvironmentError409 — active version exists, but not released to your envFall back, alert, or fail loudly
requests.exceptions.HTTPErrorNon-2xx that isn't 409 — most commonly 404Treat as "prompt unavailable"
requests.exceptions.RequestExceptionNetwork failure, timeout, DNS errorRetry with backoff
KeyErrorMalformed response (server-side bug)Should not happen in steady state; log and alert
ValueErrorBad constructor argsProgramming bug; fix at startup

409 — PromptNotReleasedInEnvironmentError

The active version exists, your key is valid, the project is real — the version just isn't released to the environment you asked for.

python
from elsai_prompts.prompt_manager import PromptNotReleasedInEnvironmentError

try:
    prompt = pm.get_active_prompt_version("welcome_email")
except PromptNotReleasedInEnvironmentError as e:
    print(e.prompt_name)             # "welcome_email"
    print(e.requested_environment)   # "production"
    print(e.available_environments)  # ["development", "testing"]

This is not a "something's broken" error — it's the platform refusing to serve unreleased content to your environment. Three reasonable responses:

Fail loudly (recommended for production)

A 409 in production usually means a release was skipped. Let the request fail with a clear error so the team notices.

python
try:
    prompt = pm.get_active_prompt_version("welcome_email")
except PromptNotReleasedInEnvironmentError:
    logger.exception("welcome_email not released to production")
    raise
Fall back to a hardcoded default

For non-critical surfaces (a footer message, an internal-only feature), graceful degradation may be appropriate.

python
try:
    prompt = pm.get_active_prompt_version("welcome_email")
    text = prompt.render({"name": user.name})
except PromptNotReleasedInEnvironmentError:
    text = f"Welcome, {user.name}!"   # safe default
Try a different environment

Rare. If you want to serve "whatever is in production" even when running in dev:

python
try:
    prompt = pm.get_active_prompt_version("welcome_email")
except PromptNotReleasedInEnvironmentError:
    prompt = pm.get_active_prompt_version_for_environment(
        "welcome_email", environment="production"
    )

This masks the intent of the env filter — only do it deliberately.

404 — HTTPError

A 404 is the server's "I can't show this to you" answer. The platform collapses several real reasons into a single 404 so attackers can't enumerate:

  • API key invalid or revoked
  • Project doesn't exist
  • Prompt doesn't exist in this project
  • No active version set on the prompt
  • On-prem only: the key's owner isn't a current member of the project's organization
python
import requests

try:
    prompt = pm.get_active_prompt_version("welcome_email")
except requests.exceptions.HTTPError as e:
    if e.response.status_code == 404:
        logger.error("prompt_unavailable: %s", "welcome_email")
    else:
        raise

Network errors

Connection refused, DNS failures, TLS errors, timeouts:

python
import requests

try:
    prompt = pm.get_active_prompt_version("welcome_email")
except requests.exceptions.Timeout:
    # Slow upstream - likely transient
    ...
except requests.exceptions.ConnectionError:
    # Can't reach the server
    ...
except requests.exceptions.RequestException:
    # Catch-all for other network/HTTP errors
    ...

Robust caller pattern

A production-grade wrapper that handles the common cases:

python
import logging
import time

import requests

from elsai_prompts.prompt_manager import (
    PromptContent,
    PromptManager,
    PromptNotReleasedInEnvironmentError,
)

logger = logging.getLogger(__name__)


def fetch_prompt(
    pm: PromptManager,
    prompt_name: str,
    *,
    retries: int = 3,
    backoff: float = 0.5,
) -> PromptContent:
    """Fetch with retries on transient network errors."""
    last_exc: Exception | None = None
    for attempt in range(retries):
        try:
            return pm.get_active_prompt_version(prompt_name)
        except PromptNotReleasedInEnvironmentError:
            raise  # not transient
        except requests.exceptions.HTTPError as e:
            if e.response is not None and 500 <= e.response.status_code < 600:
                last_exc = e  # server error, retry
            else:
                raise  # 4xx - not transient
        except (requests.exceptions.Timeout, requests.exceptions.ConnectionError) as e:
            last_exc = e

        time.sleep(backoff * (2 ** attempt))

    assert last_exc is not None
    raise last_exc

Caching to reduce blast radius

Network calls fail. If your application's hot path depends on a prompt fetch, an outage upstream is your outage too.

The simplest mitigation: cache fetches in-memory for a short TTL.

python
from datetime import datetime, timedelta, timezone

_cache: dict[str, tuple[datetime, PromptContent]] = {}
_TTL = timedelta(seconds=60)


def fetch_cached(pm, prompt_name):
    now = datetime.now(timezone.utc)
    cached = _cache.get(prompt_name)
    if cached and now - cached[0] < _TTL:
        return cached[1]

    prompt = pm.get_active_prompt_version(prompt_name)
    _cache[prompt_name] = (now, prompt)
    return prompt

TIP

A 60-second cache means prompt changes in the console take up to 60 seconds to propagate to your application. Tune TTL to the latency you can tolerate vs the resilience you want.

ValueError on construction

If you initialize PromptManager with empty required args, it raises ValueError immediately:

python
PromptManager(api_key="", project_id="x", environment="dev")
# ValueError: api_key is required.

These are programming bugs, not runtime conditions — fix the call site, don't catch them in production code.

Copyright © 2026 elsai foundry.