# Behavior traces — encode/starlette

> Canonical page: https://codevetter.com/unpack/starlette/behavior-traces

# Behavior traces — encode/starlette

Scanned commit 4e7fc04e5f2c69f60e418ff2107d0f6d9a0fe423 (2026-10-01). Generated by codevetter codevetter 1.15.0 on 2026-10-02.

Source inspection; upstream code and tests were not executed.

The inspected flows follow explicit protocol transitions rather than a standalone executable entrypoint. Startup establishes resources, HTTP routing adapts endpoints, and session persistence occurs when response headers start. Upload and static-file paths cross filesystem boundaries with distinct cleanup and containment rules. WebSocket and exception paths enforce state-dependent behavior that is material to callers.
- Startup: TestClient.__enter__ starts a BlockingPortal and lifespan task → wait_startup sends lifespan.startup → Starlette.__call__ builds its stack → Router.app selects lifespan → Router.lifespan enters the context and sends startup.complete; exit triggers shutdown and context teardown, with failures distinguished by startup state. — [starlette/testclient.py#L496-L562](https://github.com/encode/starlette/blob/4e7fc04e5f2c69f60e418ff2107d0f6d9a0fe423/starlette/testclient.py#L496-L562) [starlette/applications.py#L92-L96](https://github.com/encode/starlette/blob/4e7fc04e5f2c69f60e418ff2107d0f6d9a0fe423/starlette/applications.py#L92-L96) [starlette/routing.py#L645-L685](https://github.com/encode/starlette/blob/4e7fc04e5f2c69f60e418ff2107d0f6d9a0fe423/starlette/routing.py#L645-L685)
- HTTP success: Starlette.__call__ → middleware stack → Router.app → Route.matches → scope update → Route.handle → request_response creates Request → endpoint runs asynchronously or in a worker thread → returned Response sends headers/body and then awaits its background callback. — [starlette/applications.py#L92-L96](https://github.com/encode/starlette/blob/4e7fc04e5f2c69f60e418ff2107d0f6d9a0fe423/starlette/applications.py#L92-L96) [starlette/routing.py#L47-L65](https://github.com/encode/starlette/blob/4e7fc04e5f2c69f60e418ff2107d0f6d9a0fe423/starlette/routing.py#L47-L65) [starlette/routing.py#L244-L282](https://github.com/encode/starlette/blob/4e7fc04e5f2c69f60e418ff2107d0f6d9a0fe423/starlette/routing.py#L244-L282) [starlette/responses.py#L166-L173](https://github.com/encode/starlette/blob/4e7fc04e5f2c69f60e418ff2107d0f6d9a0fe423/starlette/responses.py#L166-L173)
- Routing fallback: Router.app returns on the first FULL match, remembers the first PARTIAL match for 405, otherwise tries a slash-adjusted scope and RedirectResponse, then calls default; unmatched WebSockets close and unmatched HTTP inside Starlette raises HTTPException(404). — [starlette/routing.py#L622-L635](https://github.com/encode/starlette/blob/4e7fc04e5f2c69f60e418ff2107d0f6d9a0fe423/starlette/routing.py#L622-L635) [starlette/routing.py#L688-L728](https://github.com/encode/starlette/blob/4e7fc04e5f2c69f60e418ff2107d0f6d9a0fe423/starlette/routing.py#L688-L728) [starlette/routing.py#L273-L282](https://github.com/encode/starlette/blob/4e7fc04e5f2c69f60e418ff2107d0f6d9a0fe423/starlette/routing.py#L273-L282)
- Upload: endpoint calls Request.form → _get_form selects MultiPartParser → parse streams chunks into callbacks → on_headers_finished creates UploadFile → queued writes and seek(0) complete → FormData returns; parser failures close temporary files, while form context exit closes successful uploads. — [starlette/requests.py#L268-L322](https://github.com/encode/starlette/blob/4e7fc04e5f2c69f60e418ff2107d0f6d9a0fe423/starlette/requests.py#L268-L322) [starlette/formparsers.py#L222-L245](https://github.com/encode/starlette/blob/4e7fc04e5f2c69f60e418ff2107d0f6d9a0fe423/starlette/formparsers.py#L222-L245) [starlette/formparsers.py#L250-L301](https://github.com/encode/starlette/blob/4e7fc04e5f2c69f60e418ff2107d0f6d9a0fe423/starlette/formparsers.py#L250-L301) [starlette/_utils.py#L92-L98](https://github.com/encode/starlette/blob/4e7fc04e5f2c69f60e418ff2107d0f6d9a0fe423/starlette/_utils.py#L92-L98)
- Session persistence: SessionMiddleware loads and verifies an incoming cookie → request.session marks access → Session.__setitem__ marks modification → endpoint returns Response → send_wrapper observes http.response.start and signs JSON into Set-Cookie; read-only access adds Vary without rewriting the cookie. — [starlette/middleware/sessions.py#L47-L91](https://github.com/encode/starlette/blob/4e7fc04e5f2c69f60e418ff2107d0f6d9a0fe423/starlette/middleware/sessions.py#L47-L91) [starlette/middleware/sessions.py#L94-L107](https://github.com/encode/starlette/blob/4e7fc04e5f2c69f60e418ff2107d0f6d9a0fe423/starlette/middleware/sessions.py#L94-L107) [starlette/requests.py#L169-L176](https://github.com/encode/starlette/blob/4e7fc04e5f2c69f60e418ff2107d0f6d9a0fe423/starlette/requests.py#L169-L176) [tests/middleware/test_session.py#L248-L289](https://github.com/encode/starlette/blob/4e7fc04e5f2c69f60e418ff2107d0f6d9a0fe423/tests/middleware/test_session.py#L248-L289)
- Static delivery: Mount updates root_path → StaticFiles.__call__ checks configuration and normalizes the path → get_response runs lookup_path in a thread → file_response selects 304 or FileResponse → FileResponse handles HEAD, pathsend, full content, or byte ranges; malformed and unsatisfiable ranges return 400 and 416. — [starlette/routing.py#L396-L427](https://github.com/encode/starlette/blob/4e7fc04e5f2c69f60e418ff2107d0f6d9a0fe423/starlette/routing.py#L396-L427) [starlette/staticfiles.py#L88-L138](https://github.com/encode/starlette/blob/4e7fc04e5f2c69f60e418ff2107d0f6d9a0fe423/starlette/staticfiles.py#L88-L138) [starlette/staticfiles.py#L181-L193](https://github.com/encode/starlette/blob/4e7fc04e5f2c69f60e418ff2107d0f6d9a0fe423/starlette/staticfiles.py#L181-L193) [starlette/responses.py#L344-L410](https://github.com/encode/starlette/blob/4e7fc04e5f2c69f60e418ff2107d0f6d9a0fe423/starlette/responses.py#L344-L410)
- WebSocket endpoint: WebSocketRoute adapts a function through websocket_session → endpoint calls WebSocket.accept, which first receives connect if necessary → receive_text/bytes/json validates accepted state → send methods validate application state → close transitions to disconnected; denial responses require the advertised server extension. — [starlette/routing.py#L70-L84](https://github.com/encode/starlette/blob/4e7fc04e5f2c69f60e418ff2107d0f6d9a0fe423/starlette/routing.py#L70-L84) [starlette/routing.py#L313-L327](https://github.com/encode/starlette/blob/4e7fc04e5f2c69f60e418ff2107d0f6d9a0fe423/starlette/routing.py#L313-L327) [starlette/websockets.py#L41-L116](https://github.com/encode/starlette/blob/4e7fc04e5f2c69f60e418ff2107d0f6d9a0fe423/starlette/websockets.py#L41-L116) [starlette/websockets.py#L122-L193](https://github.com/encode/starlette/blob/4e7fc04e5f2c69f60e418ff2107d0f6d9a0fe423/starlette/websockets.py#L122-L193)
- Error handling: ExceptionMiddleware installs handlers in scope → wrap_app_handling_exceptions chooses HTTP status then exception-MRO handlers, rejecting replacement after response start → unhandled HTTP errors reach ServerErrorMiddleware, which emits a debug/custom/default 500 only before start and always re-raises. — [starlette/middleware/exceptions.py#L47-L69](https://github.com/encode/starlette/blob/4e7fc04e5f2c69f60e418ff2107d0f6d9a0fe423/starlette/middleware/exceptions.py#L47-L69) [starlette/_exception_handler.py#L16-L63](https://github.com/encode/starlette/blob/4e7fc04e5f2c69f60e418ff2107d0f6d9a0fe423/starlette/_exception_handler.py#L16-L63) [starlette/middleware/errors.py#L149-L186](https://github.com/encode/starlette/blob/4e7fc04e5f2c69f60e418ff2107d0f6d9a0fe423/starlette/middleware/errors.py#L149-L186)

Every claim links to its cited files at the pinned commit. Citations identify source evidence; they do not establish runtime correctness. Independent CodeVetter scan — not reviewed by upstream maintainers.

## Public product links

- [CodeVetter](https://codevetter.com/)
- [Download](https://codevetter.com/download)
- [Documentation](https://codevetter.com/docs/)
- [Source](https://github.com/Codevetter/codevetter)
