Appearance
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
| Exception | When | Recovery |
|---|---|---|
PromptNotReleasedInEnvironmentError | 409 — active version exists, but not released to your env | Fall back, alert, or fail loudly |
requests.exceptions.HTTPError | Non-2xx that isn't 409 — most commonly 404 | Treat as "prompt unavailable" |
requests.exceptions.RequestException | Network failure, timeout, DNS error | Retry with backoff |
KeyError | Malformed response (server-side bug) | Should not happen in steady state; log and alert |
ValueError | Bad constructor args | Programming 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")
raiseFall 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 defaultTry 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:
raiseNetwork 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_excCaching 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 promptTIP
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.