Files
docs/openapi-first/wiki/06_error_handling/index.html

1068 lines
32 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!doctype html>
<html lang="en" class="no-js">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<link rel="prev" href="../05_security/">
<link rel="next" href="../07_testing/">
<link rel="icon" href="../assets/images/favicon.png">
<meta name="generator" content="mkdocs-1.6.1, mkdocs-material-9.6.23">
<title>Error Handling - openapi_first</title>
<link rel="stylesheet" href="../assets/stylesheets/main.84d31ad4.min.css">
<link rel="stylesheet" href="../assets/stylesheets/palette.06af60db.min.css">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto:300,300i,400,400i,700,700i%7CJetBrains+Mono:400,400i,700,700i&display=fallback">
<style>:root{--md-text-font:"Roboto";--md-code-font:"JetBrains Mono"}</style>
<link rel="stylesheet" href="https://unpkg.com/dracula-prism/dist/css/dracula-prism.css">
<script>__md_scope=new URL("..",location),__md_hash=e=>[...e].reduce(((e,_)=>(e<<5)-e+_.charCodeAt(0)),0),__md_get=(e,_=localStorage,t=__md_scope)=>JSON.parse(_.getItem(t.pathname+"."+e)),__md_set=(e,_,t=localStorage,a=__md_scope)=>{try{t.setItem(a.pathname+"."+e,JSON.stringify(_))}catch(e){}}</script>
</head>
<body dir="ltr" data-md-color-scheme="slate" data-md-color-primary="blue-grey" data-md-color-accent="teal">
<input class="md-toggle" data-md-toggle="drawer" type="checkbox" id="__drawer" autocomplete="off">
<input class="md-toggle" data-md-toggle="search" type="checkbox" id="__search" autocomplete="off">
<label class="md-overlay" for="__drawer"></label>
<div data-md-component="skip">
<a href="#error-handling-fail-loud-fail-early" class="md-skip">
Skip to content
</a>
</div>
<div data-md-component="announce">
</div>
<header class="md-header" data-md-component="header">
<nav class="md-header__inner md-grid" aria-label="Header">
<a href=".." title="openapi_first" class="md-header__button md-logo" aria-label="openapi_first" data-md-component="logo">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M7 7H5a2 2 0 0 0-2 2v8h2v-4h2v4h2V9a2 2 0 0 0-2-2m0 4H5V9h2m7-2h-4v10h2v-4h2a2 2 0 0 0 2-2V9a2 2 0 0 0-2-2m0 4h-2V9h2m6 0v6h1v2h-4v-2h1V9h-1V7h4v2Z"/></svg>
</a>
<label class="md-header__button md-icon" for="__drawer">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M3 6h18v2H3zm0 5h18v2H3zm0 5h18v2H3z"/></svg>
</label>
<div class="md-header__title" data-md-component="header-title">
<div class="md-header__ellipsis">
<div class="md-header__topic">
<span class="md-ellipsis">
openapi_first
</span>
</div>
<div class="md-header__topic" data-md-component="header-topic">
<span class="md-ellipsis">
Error Handling
</span>
</div>
</div>
</div>
<label class="md-header__button md-icon" for="__search">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M9.5 3A6.5 6.5 0 0 1 16 9.5c0 1.61-.59 3.09-1.56 4.23l.27.27h.79l5 5-1.5 1.5-5-5v-.79l-.27-.27A6.52 6.52 0 0 1 9.5 16 6.5 6.5 0 0 1 3 9.5 6.5 6.5 0 0 1 9.5 3m0 2C7 5 5 7 5 9.5S7 14 9.5 14 14 12 14 9.5 12 5 9.5 5"/></svg>
</label>
<div class="md-search" data-md-component="search" role="dialog">
<label class="md-search__overlay" for="__search"></label>
<div class="md-search__inner" role="search">
<form class="md-search__form" name="search">
<input type="text" class="md-search__input" name="query" aria-label="Search" placeholder="Search" autocapitalize="off" autocorrect="off" autocomplete="off" spellcheck="false" data-md-component="search-query" required>
<label class="md-search__icon md-icon" for="__search">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M9.5 3A6.5 6.5 0 0 1 16 9.5c0 1.61-.59 3.09-1.56 4.23l.27.27h.79l5 5-1.5 1.5-5-5v-.79l-.27-.27A6.52 6.52 0 0 1 9.5 16 6.5 6.5 0 0 1 3 9.5 6.5 6.5 0 0 1 9.5 3m0 2C7 5 5 7 5 9.5S7 14 9.5 14 14 12 14 9.5 12 5 9.5 5"/></svg>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M20 11v2H8l5.5 5.5-1.42 1.42L4.16 12l7.92-7.92L13.5 5.5 8 11z"/></svg>
</label>
<nav class="md-search__options" aria-label="Search">
<a href="javascript:void(0)" class="md-search__icon md-icon" title="Share" aria-label="Share" data-clipboard data-clipboard-text="" data-md-component="search-share" tabindex="-1">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M18 16.08c-.76 0-1.44.3-1.96.77L8.91 12.7c.05-.23.09-.46.09-.7s-.04-.47-.09-.7l7.05-4.11c.54.5 1.25.81 2.04.81a3 3 0 0 0 3-3 3 3 0 0 0-3-3 3 3 0 0 0-3 3c0 .24.04.47.09.7L8.04 9.81C7.5 9.31 6.79 9 6 9a3 3 0 0 0-3 3 3 3 0 0 0 3 3c.79 0 1.5-.31 2.04-.81l7.12 4.15c-.05.21-.08.43-.08.66 0 1.61 1.31 2.91 2.92 2.91s2.92-1.3 2.92-2.91A2.92 2.92 0 0 0 18 16.08"/></svg>
</a>
<button type="reset" class="md-search__icon md-icon" title="Clear" aria-label="Clear" tabindex="-1">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M19 6.41 17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z"/></svg>
</button>
</nav>
<div class="md-search__suggest" data-md-component="search-suggest"></div>
</form>
<div class="md-search__output">
<div class="md-search__scrollwrap" tabindex="0" data-md-scrollfix>
<div class="md-search-result" data-md-component="search-result">
<div class="md-search-result__meta">
Initializing search
</div>
<ol class="md-search-result__list" role="presentation"></ol>
</div>
</div>
</div>
</div>
</div>
</nav>
</header>
<div class="md-container" data-md-component="container">
<nav class="md-tabs" aria-label="Tabs" data-md-component="tabs">
<div class="md-grid">
<ul class="md-tabs__list">
<li class="md-tabs__item">
<a href=".." class="md-tabs__link">
Home
</a>
</li>
<li class="md-tabs__item">
<a href="../01_overview/" class="md-tabs__link">
Overview
</a>
</li>
<li class="md-tabs__item">
<a href="../02_components/" class="md-tabs__link">
Components
</a>
</li>
<li class="md-tabs__item">
<a href="../03_use_cases/01_quickstart/" class="md-tabs__link">
Use Cases
</a>
</li>
<li class="md-tabs__item">
<a href="../04_design/" class="md-tabs__link">
Design
</a>
</li>
<li class="md-tabs__item">
<a href="../05_security/" class="md-tabs__link">
Security
</a>
</li>
<li class="md-tabs__item md-tabs__item--active">
<a href="./" class="md-tabs__link">
Error Handling
</a>
</li>
<li class="md-tabs__item">
<a href="../07_testing/" class="md-tabs__link">
Testing
</a>
</li>
</ul>
</div>
</nav>
<main class="md-main" data-md-component="main">
<div class="md-main__inner md-grid">
<div class="md-sidebar md-sidebar--primary" data-md-component="sidebar" data-md-type="navigation" >
<div class="md-sidebar__scrollwrap">
<div class="md-sidebar__inner">
<nav class="md-nav md-nav--primary md-nav--lifted md-nav--integrated" aria-label="Navigation" data-md-level="0">
<label class="md-nav__title" for="__drawer">
<a href=".." title="openapi_first" class="md-nav__button md-logo" aria-label="openapi_first" data-md-component="logo">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M7 7H5a2 2 0 0 0-2 2v8h2v-4h2v4h2V9a2 2 0 0 0-2-2m0 4H5V9h2m7-2h-4v10h2v-4h2a2 2 0 0 0 2-2V9a2 2 0 0 0-2-2m0 4h-2V9h2m6 0v6h1v2h-4v-2h1V9h-1V7h4v2Z"/></svg>
</a>
openapi_first
</label>
<ul class="md-nav__list" data-md-scrollfix>
<li class="md-nav__item">
<a href=".." class="md-nav__link">
<span class="md-ellipsis">
Home
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../01_overview/" class="md-nav__link">
<span class="md-ellipsis">
Overview
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../02_components/" class="md-nav__link">
<span class="md-ellipsis">
Components
</span>
</a>
</li>
<li class="md-nav__item md-nav__item--nested">
<input class="md-nav__toggle md-toggle md-toggle--indeterminate" type="checkbox" id="__nav_4" >
<label class="md-nav__link" for="__nav_4" id="__nav_4_label" tabindex="0">
<span class="md-ellipsis">
Use Cases
</span>
<span class="md-nav__icon md-icon"></span>
</label>
<nav class="md-nav" data-md-level="1" aria-labelledby="__nav_4_label" aria-expanded="false">
<label class="md-nav__title" for="__nav_4">
<span class="md-nav__icon md-icon"></span>
Use Cases
</label>
<ul class="md-nav__list" data-md-scrollfix>
<li class="md-nav__item">
<a href="../03_use_cases/01_quickstart/" class="md-nav__link">
<span class="md-ellipsis">
Quickstart
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../03_use_cases/02_templates/" class="md-nav__link">
<span class="md-ellipsis">
Templates
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../03_use_cases/03_client/" class="md-nav__link">
<span class="md-ellipsis">
Client
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../03_use_cases/04_codegen/" class="md-nav__link">
<span class="md-ellipsis">
Codegen
</span>
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item">
<a href="../04_design/" class="md-nav__link">
<span class="md-ellipsis">
Design
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../05_security/" class="md-nav__link">
<span class="md-ellipsis">
Security
</span>
</a>
</li>
<li class="md-nav__item md-nav__item--active">
<input class="md-nav__toggle md-toggle" type="checkbox" id="__toc">
<label class="md-nav__link md-nav__link--active" for="__toc">
<span class="md-ellipsis">
Error Handling
</span>
<span class="md-nav__icon md-icon"></span>
</label>
<a href="./" class="md-nav__link md-nav__link--active">
<span class="md-ellipsis">
Error Handling
</span>
</a>
<nav class="md-nav md-nav--secondary" aria-label="Table of contents">
<label class="md-nav__title" for="__toc">
<span class="md-nav__icon md-icon"></span>
Table of contents
</label>
<ul class="md-nav__list" data-md-component="toc" data-md-scrollfix>
<li class="md-nav__item">
<a href="#1-the-hierarchy" class="md-nav__link">
<span class="md-ellipsis">
🧬 1. The Hierarchy
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#2-when-things-fail" class="md-nav__link">
<span class="md-ellipsis">
⏱️ 2. When Things Fail
</span>
</a>
<nav class="md-nav" aria-label="⏱️ 2. When Things Fail">
<ul class="md-nav__list">
<li class="md-nav__item">
<a href="#21-at-application-startup" class="md-nav__link">
<span class="md-ellipsis">
2.1 At application startup
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#22-at-client-construction" class="md-nav__link">
<span class="md-ellipsis">
2.2 At client construction
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#23-at-call-time-client" class="md-nav__link">
<span class="md-ellipsis">
2.3 At call time (client)
</span>
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item">
<a href="#3-handling-in-your-app" class="md-nav__link">
<span class="md-ellipsis">
🧰 3. Handling in Your App
</span>
</a>
<nav class="md-nav" aria-label="🧰 3. Handling in Your App">
<ul class="md-nav__list">
<li class="md-nav__item">
<a href="#31-server-side" class="md-nav__link">
<span class="md-ellipsis">
3.1 Server-side
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#32-client-side" class="md-nav__link">
<span class="md-ellipsis">
3.2 Client-side
</span>
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item">
<a href="#4-fail-fast-guarantees-recap" class="md-nav__link">
<span class="md-ellipsis">
🛡️ 4. Fail-Fast Guarantees Recap
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#related" class="md-nav__link">
<span class="md-ellipsis">
Related
</span>
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item">
<a href="../07_testing/" class="md-nav__link">
<span class="md-ellipsis">
Testing
</span>
</a>
</li>
</ul>
</nav>
</div>
</div>
</div>
<div class="md-content" data-md-component="content">
<article class="md-content__inner md-typeset">
<h1 id="error-handling-fail-loud-fail-early">Error Handling — Fail Loud, Fail Early</h1>
<p><code>openapi-first</code> treats errors as <strong>first-class contract documents</strong>: every failure mode is a named exception with a stable import pathhare, and every one surfaces as early as possible.</p>
<hr />
<h2 id="1-the-hierarchy">🧬 1. The Hierarchy</h2>
<p>All errors derive from <code>OpenAPIFirstError</code> (in <code>openapi_first/errors.py</code>), so a single <code>except OpenAPIFirstError</code> catches every first-party failure:</p>
<div class="language-text highlight"><table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre><span></span><span class="normal"><a href="#__codelineno-0-1">1</a></span>
<span class="normal"><a href="#__codelineno-0-2">2</a></span>
<span class="normal"><a href="#__codelineno-0-3">3</a></span>
<span class="normal"><a href="#__codelineno-0-4">4</a></span>
<span class="normal"><a href="#__codelineno-0-5">5</a></span>
<span class="normal"><a href="#__codelineno-0-6">6</a></span>
<span class="normal"><a href="#__codelineno-0-7">7</a></span></pre></div></td><td class="code"><div><pre><span></span><code><span id="__span-0-1"><a id="__codelineno-0-1" name="__codelineno-0-1"></a>OpenAPIFirstError
</span><span id="__span-0-2"><a id="__codelineno-0-2" name="__codelineno-0-2"></a>├── OpenAPISpecError # spec-level problems
</span><span id="__span-0-3"><a id="__codelineno-0-3" name="__codelineno-0-3"></a>│ └── OpenAPISpecLoadError # load / parse / validation (loader)
</span><span id="__span-0-4"><a id="__codelineno-0-4" name="__codelineno-0-4"></a>├── OpenAPIClientError # client-side contract issues (client)
</span><span id="__span-0-5"><a id="__codelineno-0-5" name="__codelineno-0-5"></a>└── MissingOperationHandler # spec op with no handler (binder)
</span><span id="__span-0-6"><a id="__codelineno-0-6" name="__codelineno-0-6"></a>
</span><span id="__span-0-7"><a id="__codelineno-0-7" name="__codelineno-0-7"></a># Security / loader layers raise through OpenAPISpecError subclasses too
</span></code></pre></div></td></tr></table></div>
<table>
<thead>
<tr>
<th>Exception</th>
<th>Module</th>
<th>Raised when</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>OpenAPISpecLoadError</code></td>
<td><code>loader</code></td>
<td>Path missing, file unreadable, YAML/JSON invalid, or spec fails OpenAPI 3.x validation</td>
</tr>
<tr>
<td><code>OpenAPIClientError</code></td>
<td><code>client</code></td>
<td>No <code>servers</code>, no <code>paths</code>, missing/duplicate <code>operationId</code>, missing required params at construction</td>
</tr>
<tr>
<td><code>MissingOperationHandler</code></td>
<td><code>errors</code></td>
<td>An operation is declared whose <code>operationId</code> has no matching handler in <code>routes_module</code></td>
</tr>
</tbody>
</table>
<hr />
<h2 id="2-when-things-fail">⏱️ 2. When Things Fail</h2>
<p>The single most important rule: <strong>violations are eager, not lazy.</strong></p>
<h3 id="21-at-application-startup">2.1 At application startup</h3>
<div class="language-python highlight"><table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre><span></span><span class="normal"><a href="#__codelineno-1-1">1</a></span>
<span class="normal"><a href="#__codelineno-1-2">2</a></span>
<span class="normal"><a href="#__codelineno-1-3">3</a></span></pre></div></td><td class="code"><div><pre><span></span><code><span id="__span-1-1"><a id="__codelineno-1-1" name="__codelineno-1-1"></a><span class="c1"># openapi.yaml missing ────────────────────────────► OpenAPISpecLoadError</span>
</span><span id="__span-1-2"><a id="__codelineno-1-2" name="__codelineno-1-2"></a><span class="c1"># operationId without a handler ───────────────────► MissingOperationHandler</span>
</span><span id="__span-1-3"><a id="__codelineno-1-3" name="__codelineno-1-3"></a><span class="c1"># operation with no operationId declared ──────────► MissingOperationHandler</span>
</span></code></pre></div></td></tr></table></div>
<p>Because these raise during <code>OpenAPIFirstApp(...)</code> construction, CI catches them the moment a spec and its routes drift — before a single request is served.</p>
<h3 id="22-at-client-construction">2.2 At client construction</h3>
<div class="language-python highlight"><table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre><span></span><span class="normal"><a href="#__codelineno-2-1">1</a></span></pre></div></td><td class="code"><div><pre><span></span><code><span id="__span-2-1"><a id="__codelineno-2-1" name="__codelineno-2-1"></a><span class="n">OpenAPIClient</span><span class="p">(</span><span class="n">spec</span><span class="p">)</span> <span class="c1"># fails fast, same philosophy</span>
</span></code></pre></div></td></tr></table></div>
<ul>
<li>Spec with no <code>servers</code><code>OpenAPIClientError</code></li>
<li>Spec with no <code>paths</code><code>OpenAPIClientError</code></li>
<li>Duplicate <code>operationId</code>s → <code>OpenAPIClientError</code> (client methods must be unambiguous)</li>
<li>Operation missing <code>operationId</code><code>OpenAPIClientError</code></li>
</ul>
<h3 id="23-at-call-time-client">2.3 At call time (client)</h3>
<p>Runtime transport errors surface as <code>httpx</code> exceptions (<code>httpx.RequestError</code> family), not swallowed or remapped. Missing required args fail before any HTTP request is made:</p>
<div class="language-python highlight"><table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre><span></span><span class="normal"><a href="#__codelineno-3-1">1</a></span>
<span class="normal"><a href="#__codelineno-3-2">2</a></span></pre></div></td><td class="code"><div><pre><span></span><code><span id="__span-3-1"><a id="__codelineno-3-1" name="__codelineno-3-1"></a><span class="n">client</span><span class="o">.</span><span class="n">get_user</span><span class="p">(</span><span class="n">path_params</span><span class="o">=</span><span class="p">{</span><span class="s2">"user_id"</span><span class="p">:</span> <span class="o">...</span><span class="p">})</span> <span class="c1"># OK</span>
</span><span id="__span-3-2"><a id="__codelineno-3-2" name="__codelineno-3-2"></a><span class="n">client</span><span class="o">.</span><span class="n">get_user</span><span class="p">()</span> <span class="c1"># ValueError — user_id required</span>
</span></code></pre></div></td></tr></table></div>
<hr />
<h2 id="3-handling-in-your-app">🧰 3. Handling in Your App</h2>
<h3 id="31-server-side">3.1 Server-side</h3>
<p>Handlers raise FastAPI <code>HTTPException</code> for expected operation-level failures (404/422), and the <code>OperationId</code>-binding errors only exist at startup:</p>
<div class="language-python highlight"><table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre><span></span><span class="normal"><a href="#__codelineno-4-1"> 1</a></span>
<span class="normal"><a href="#__codelineno-4-2"> 2</a></span>
<span class="normal"><a href="#__codelineno-4-3"> 3</a></span>
<span class="normal"><a href="#__codelineno-4-4"> 4</a></span>
<span class="normal"><a href="#__codelineno-4-5"> 5</a></span>
<span class="normal"><a href="#__codelineno-4-6"> 6</a></span>
<span class="normal"><a href="#__codelineno-4-7"> 7</a></span>
<span class="normal"><a href="#__codelineno-4-8"> 8</a></span>
<span class="normal"><a href="#__codelineno-4-9"> 9</a></span>
<span class="normal"><a href="#__codelineno-4-10">10</a></span>
<span class="normal"><a href="#__codelineno-4-11">11</a></span>
<span class="normal"><a href="#__codelineno-4-12">12</a></span>
<span class="normal"><a href="#__codelineno-4-13">13</a></span>
<span class="normal"><a href="#__codelineno-4-14">14</a></span>
<span class="normal"><a href="#__codelineno-4-15">15</a></span>
<span class="normal"><a href="#__codelineno-4-16">16</a></span>
<span class="normal"><a href="#__codelineno-4-17">17</a></span></pre></div></td><td class="code"><div><pre><span></span><code><span id="__span-4-1"><a id="__codelineno-4-1" name="__codelineno-4-1"></a><span class="kn">from</span><span class="w"> </span><span class="nn">fastapi</span><span class="w"> </span><span class="kn">import</span> <span class="n">HTTPException</span>
</span><span id="__span-4-2"><a id="__codelineno-4-2" name="__codelineno-4-2"></a>
</span><span id="__span-4-3"><a id="__codelineno-4-3" name="__codelineno-4-3"></a><span class="k">def</span><span class="w"> </span><span class="nf">get_item</span><span class="p">(</span><span class="n">item_id</span><span class="p">:</span> <span class="nb">int</span><span class="p">):</span>
</span><span id="__span-4-4"><a id="__codelineno-4-4" name="__codelineno-4-4"></a><span class="w"> </span><span class="sd">"""Retrieve an item by ID.</span>
</span><span id="__span-4-5"><a id="__codelineno-4-5" name="__codelineno-4-5"></a>
</span><span id="__span-4-6"><a id="__codelineno-4-6" name="__codelineno-4-6"></a><span class="sd"> Implements the OpenAPI operation ``get_item``.</span>
</span><span id="__span-4-7"><a id="__codelineno-4-7" name="__codelineno-4-7"></a>
</span><span id="__span-4-8"><a id="__codelineno-4-8" name="__codelineno-4-8"></a><span class="sd"> Args:</span>
</span><span id="__span-4-9"><a id="__codelineno-4-9" name="__codelineno-4-9"></a><span class="sd"> item_id (int): Identifier of the item.</span>
</span><span id="__span-4-10"><a id="__codelineno-4-10" name="__codelineno-4-10"></a>
</span><span id="__span-4-11"><a id="__codelineno-4-11" name="__codelineno-4-11"></a><span class="sd"> Raises:</span>
</span><span id="__span-4-12"><a id="__codelineno-4-12" name="__codelineno-4-12"></a><span class="sd"> HTTPException: If the item does not exist (404).</span>
</span><span id="__span-4-13"><a id="__codelineno-4-13" name="__codelineno-4-13"></a><span class="sd"> """</span>
</span><span id="__span-4-14"><a id="__codelineno-4-14" name="__codelineno-4-14"></a> <span class="k">try</span><span class="p">:</span>
</span><span id="__span-4-15"><a id="__codelineno-4-15" name="__codelineno-4-15"></a> <span class="k">return</span> <span class="n">_get_item</span><span class="p">(</span><span class="n">item_id</span><span class="p">)</span>
</span><span id="__span-4-16"><a id="__codelineno-4-16" name="__codelineno-4-16"></a> <span class="k">except</span> <span class="ne">KeyError</span><span class="p">:</span>
</span><span id="__span-4-17"><a id="__codelineno-4-17" name="__codelineno-4-17"></a> <span class="k">raise</span> <span class="n">HTTPException</span><span class="p">(</span><span class="n">status_code</span><span class="o">=</span><span class="mi">404</span><span class="p">,</span> <span class="n">detail</span><span class="o">=</span><span class="s2">"Item not found"</span><span class="p">)</span>
</span></code></pre></div></td></tr></table></div>
<h3 id="32-client-side">3.2 Client-side</h3>
<div class="language-python highlight"><table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre><span></span><span class="normal"><a href="#__codelineno-5-1">1</a></span>
<span class="normal"><a href="#__codelineno-5-2">2</a></span>
<span class="normal"><a href="#__codelineno-5-3">3</a></span>
<span class="normal"><a href="#__codelineno-5-4">4</a></span>
<span class="normal"><a href="#__codelineno-5-5">5</a></span>
<span class="normal"><a href="#__codelineno-5-6">6</a></span></pre></div></td><td class="code"><div><pre><span></span><code><span id="__span-5-1"><a id="__codelineno-5-1" name="__codelineno-5-1"></a><span class="kn">import</span><span class="w"> </span><span class="nn">httpx</span>
</span><span id="__span-5-2"><a id="__codelineno-5-2" name="__codelineno-5-2"></a>
</span><span id="__span-5-3"><a id="__codelineno-5-3" name="__codelineno-5-3"></a><span class="k">try</span><span class="p">:</span>
</span><span id="__span-5-4"><a id="__codelineno-5-4" name="__codelineno-5-4"></a> <span class="n">response</span> <span class="o">=</span> <span class="n">client</span><span class="o">.</span><span class="n">get_item</span><span class="p">(</span><span class="n">path_params</span><span class="o">=</span><span class="p">{</span><span class="s2">"item_id"</span><span class="p">:</span> <span class="mi">1</span><span class="p">})</span>
</span><span id="__span-5-5"><a id="__codelineno-5-5" name="__codelineno-5-5"></a><span class="k">except</span> <span class="n">httpx</span><span class="o">.</span><span class="n">HTTPStatusError</span> <span class="k">as</span> <span class="n">exc</span><span class="p">:</span>
</span><span id="__span-5-6"><a id="__codelineno-5-6" name="__codelineno-5-6"></a> <span class="o">...</span> <span class="c1"># 4xx/5xx from the server</span>
</span></code></pre></div></td></tr></table></div>
<p><code>httpx.HTTPStatusError</code> isn't raised by the library — it's the standard <code>httpx.raise_for_status()</code> you can opt into per call. The library never masks a response code.</p>
<hr />
<h2 id="4-fail-fast-guarantees-recap">🛡️ 4. Fail-Fast Guarantees Recap</h2>
<table>
<thead>
<tr>
<th>Layer</th>
<th>You write</th>
<th>The library guarantees</th>
</tr>
</thead>
<tbody>
<tr>
<td>Loader</td>
<td>a spec path</td>
<td>unreadable/invalid specs never reach your app</td>
</tr>
<tr>
<td>Binder</td>
<td>handler functions</td>
<td>every operation must resolve, or the app won't start</td>
</tr>
<tr>
<td>Client</td>
<td>a spec</td>
<td>every operationId becomes a callable; missing ones fail at construction</td>
</tr>
<tr>
<td>Runtime</td>
<td>handler code</td>
<td>FastAPI + Pydantic handle coercion; contract checks already happened at startup</td>
</tr>
</tbody>
</table>
<p>None of these can silently degrade: a violation is an <strong>exception at construction</strong>, not a 500 at request time.</p>
<hr />
<h2 id="related">Related</h2>
<ul>
<li><a href="../01_overview/">01 Overview</a> · <a href="../05_security/">05 Security</a> · <a href="../07_testing/">07 Testing</a></li>
</ul>
</article>
</div>
<script>var tabs=__md_get("__tabs");if(Array.isArray(tabs))e:for(var set of document.querySelectorAll(".tabbed-set")){var labels=set.querySelector(".tabbed-labels");for(var tab of tabs)for(var label of labels.getElementsByTagName("label"))if(label.innerText.trim()===tab){var input=document.getElementById(label.htmlFor);input.checked=!0;continue e}}</script>
<script>var target=document.getElementById(location.hash.slice(1));target&&target.name&&(target.checked=target.name.startsWith("__tabbed_"))</script>
</div>
<button type="button" class="md-top md-icon" data-md-component="top" hidden>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M13 20h-2V8l-5.5 5.5-1.42-1.42L12 4.16l7.92 7.92-1.42 1.42L13 8z"/></svg>
Back to top
</button>
</main>
<footer class="md-footer">
<div class="md-footer-meta md-typeset">
<div class="md-footer-meta__inner md-grid">
<div class="md-copyright">
Made with
<a href="https://squidfunk.github.io/mkdocs-material/" target="_blank" rel="noopener">
Material for MkDocs
</a>
</div>
</div>
</div>
</footer>
</div>
<div class="md-dialog" data-md-component="dialog">
<div class="md-dialog__inner md-typeset"></div>
</div>
<script id="__config" type="application/json">{"base": "..", "features": ["navigation.sections", "navigation.expand", "navigation.top", "navigation.instant", "navigation.tracking", "navigation.indexes", "content.code.copy", "content.code.annotate", "content.tabs.link", "content.action.edit", "search.highlight", "search.share", "search.suggest", "navigation.tabs", "toc.integrate", "header.autohide", "announce.dismiss", "footer.social", "content.code.select", "content.code.line_numbers", "content.tooltips"], "search": "../assets/javascripts/workers/search.973d3a69.min.js", "tags": null, "translations": {"clipboard.copied": "Copied to clipboard", "clipboard.copy": "Copy to clipboard", "search.result.more.one": "1 more on this page", "search.result.more.other": "# more on this page", "search.result.none": "No matching documents", "search.result.one": "1 matching document", "search.result.other": "# matching documents", "search.result.placeholder": "Type to start searching", "search.result.term.missing": "Missing", "select.version": "Select version"}, "version": null}</script>
<script src="../assets/javascripts/bundle.f55a23d4.min.js"></script>
</body>
</html>