Error Handling Guide¶
Build robust services with comprehensive error handling.
Automatic Error Reporting¶
Exceptions are automatically caught and reported:
def process_job(req: Request, ctxt: JobContext) -> Result:
# Any exception here is automatically reported to the platform
result = process(req) # If this raises, it's reported as a job failure
return Result(result=result)
Manual Error Reporting¶
Explicitly report errors within named steps:
def process_job(req: Request, ctxt: JobContext) -> Result:
with ctxt.report.step("processing", message="Processing") as step:
try:
result = process(req)
except ValueError as e:
logger.error(f"Invalid input: {e}")
raise
except Exception as e:
logger.error(f"Unexpected error: {e}", exc_info=True)
raise
return Result(result=result)
Try-Except Patterns¶
Recover from Specific Errors¶
Use typed exceptions from ivcap_client.exception for platform-specific errors:
from ivcap_client.exception import ResourceNotFound, IvcapApiError
def process_job(req: Request, ctxt: JobContext) -> Result:
ivcap = ctxt.ivcap
with ctxt.report.step("download", message="Downloading artifact") as step:
try:
artifact = ivcap.get_artifact(req.artifact_urn)
with artifact.as_local_file() as path:
data = path.read_bytes()
step.finished(message=f"Downloaded {len(data)} bytes")
except ResourceNotFound:
logger.warning(f"Artifact not found: {req.artifact_urn}, using default data")
step.finished(message="Using default data")
data = DEFAULT_DATA
except IvcapApiError as e:
logger.error(f"Platform API error [{e.status_code}]: {e}")
raise
return Result(result=process(data))
Retry Logic¶
import time
def process_job(req: Request, ctxt: JobContext) -> Result:
ivcap = ctxt.ivcap
max_retries = 3
downstream = ivcap.get_service_by_name("my-downstream-service")
DownstreamReq = downstream.request_model
for attempt in range(max_retries):
try:
with ctxt.report.step(f"attempt-{attempt+1}", message=f"Attempt {attempt+1}") as step:
job = downstream.request_job(DownstreamReq(data=req.data), timeout=120)
step.finished(message="Succeeded")
return Result(result=job.result)
except Exception as e:
if attempt < max_retries - 1:
wait_time = 2 ** attempt
logger.warning(f"Attempt {attempt+1} failed, retrying in {wait_time}s: {e}")
time.sleep(wait_time)
else:
logger.error(f"All {max_retries} attempts failed")
raise
Graceful Degradation¶
Try a high-quality service and fall back gracefully if it's unavailable:
from ivcap_client.exception import ResourceNotFound, IvcapApiError
def process_job(req: Request, ctxt: JobContext) -> Result:
ivcap = ctxt.ivcap
with ctxt.report.step("process", message="Processing") as step:
try:
hq_svc = ivcap.get_service_by_name("hq-processor")
HqReq = hq_svc.request_model
job = hq_svc.request_job(HqReq(data=req.data), timeout=120)
result = job.result
step.finished(message="High-quality processing complete")
except (ResourceNotFound, IvcapApiError) as e:
logger.warning(f"HQ service unavailable ({e}), using fallback")
step.info(event={"fallback": True})
fallback_svc = ivcap.get_service_by_name("fallback-processor")
FallbackReq = fallback_svc.request_model
job = fallback_svc.request_job(FallbackReq(data=req.data), timeout=60)
result = job.result
step.finished(message="Fallback processing complete")
return Result(result=result)
Validation Errors¶
Pydantic validates request models automatically:
from pydantic import BaseModel, Field, field_validator
from ivcap_service import with_schema
@with_schema("urn:sd:schema:my-service.request.1")
class Request(BaseModel):
email: str
age: int = Field(ge=0, le=150)
@field_validator("email")
@classmethod
def validate_email(cls, v):
if "@" not in v:
raise ValueError("Invalid email format")
return v
def process_job(req: Request, ctxt: JobContext) -> Result:
# Request is guaranteed to be valid here
# Invalid requests cause the job to fail before this function is called
return Result(result=f"Processing {req.email}")
Cleanup on Error¶
Use as_local_file() as a context manager — temporary files are cleaned up
automatically on context exit. For other resources, use try/finally:
import io
def process_job(req: Request, ctxt: JobContext) -> Result:
ivcap = ctxt.ivcap
artifact = ivcap.get_artifact(req.input_urn)
# as_local_file() auto-deletes the temp file on exit, even on exception
with artifact.as_local_file() as path:
result = process_file(path)
return Result(result=result)
For non-artifact temporary resources:
import tempfile, os
def process_job(req: Request, ctxt: JobContext) -> Result:
tmp_file = None
try:
with tempfile.NamedTemporaryFile(delete=False, suffix=".tmp") as tmp:
tmp_file = tmp.name
tmp.write(generate_data(req))
result = process_file(tmp_file)
except Exception as e:
logger.error(f"Processing failed: {e}", exc_info=True)
raise
finally:
if tmp_file and os.path.exists(tmp_file):
os.unlink(tmp_file)
return Result(result=result)
Partial Success Handling¶
Continue processing items even when individual ones fail:
def process_job(req: Request, ctxt: JobContext) -> Result:
results = []
errors = []
with ctxt.report.step("batch_processing", message="Processing batch") as step:
for i, item in enumerate(req.items):
try:
result = process_item(item)
results.append(result)
except Exception as e:
errors.append({
"index": i,
"item": str(item),
"error": str(e)
})
logger.warning(f"Failed to process item {i}: {e}")
step.finished(message=f"Processed {len(results)}/{len(req.items)} items successfully")
return Result(
results=results,
errors=errors,
success=len(errors) == 0,
)
Custom Error Types¶
class ProcessingError(Exception):
"""Base error for processing failures."""
pass
class ValidationError(ProcessingError):
"""Input validation failed."""
pass
class ResourceError(ProcessingError):
"""Required resource not available."""
pass
def process_job(req: Request, ctxt: JobContext) -> Result:
try:
if not validate(req):
raise ValidationError("Invalid input data")
return Result(result=process(req))
except ValidationError as e:
logger.error(f"Validation failed: {e}")
raise
except ResourceError as e:
logger.error(f"Resource error: {e}")
raise
except Exception as e:
logger.error(f"Unexpected error: {e}", exc_info=True)
raise
See Also¶
- Job Processing — Core patterns
- Observability — Logging and monitoring
- Best Practices — Production patterns
- ivcap-client SDK — Error Handling — Client exception reference