fix(frontend): map video material HTTP 403 to 4xx (#14911)
Overview
When a video-generation worker cannot download a user-supplied material URI (HTTP 403, expired signature, private bucket), Dynamo was answering HTTP 500 with error_type/error_class internal. That counted as a service failure even though the request material is the problem.
This change maps those client/material HTTP 4xx failures to HTTP 4xx and metrics validation, and leaves genuine internal failures as HTTP 500.
Details
urllib.error.HTTPError carries .code and .msg, not .message. extract_http_like_error now reads those attributes and never falls back to str(exc), so signed URLs stay out of the client body. Clippy needless_borrow on those attribute reads is fixed (value is already &Bound).
HTTP-like Python 4xx errors are classified through the DEP 14354 catalog. A material 403 becomes ErrorClass::PermissionDenied (reason request.permission_denied, metrics ErrorType::Validation), not Internal. Unclassified worker panics still become HTTP 500/internal.
Issue #14911 asked for a distinct material-unavailable class. That is not a catalog class after DEP 14354; adding one would be a catalog/DEP change. PermissionDenied is the catalog mapping for HTTP 403 and already keeps these failures out of service-health internal metrics.
On /v1/videos streaming, the handler now waits UntilFirstEvent before committing SSE 200, so a first-event material 403 is HTTP 403 rather than 200/internal.
On the OSS SGLang video handler, material-fetch 4xx errors are re-raised so the frontend can map them.
Where should the reviewer start?
Start with extract_http_like_error in lib/bindings/python/rust/errors.rs, then the videos streaming pre-commit path in lib/llm/src/http/service/openai.rs. HTTP proof: test_videos_material_http_403_is_4xx_not_internal, test_videos_streaming_material_http_403_is_4xx_not_internal, and test_videos_internal_error_stays_500 in lib/llm/tests/http-service.rs.
Related Issues
Summary
- Rebased onto latest
ai-dynamo/dynamomain. - Fixed rust-clippy (
lib/bindings/python):clippy::needless_borrowinextract_http_like_error. - Addressed dynamo-review-agent items: streaming pre-commit backend-error check; removed redundant envelope unit test; no restating test banners.
- Catalog classification for material 403 is
PermissionDenied/ Validation, not a newmaterial-unavailableclass.
Validation
cargo clippy --no-deps --all-targets -- -D warningsinlib/bindings/python(the previously failing job).cargo test --lib http_likeandhttp_statuses_map_to_semantic_classesinlib/bindings/python.cargo test -p dynamo-llm --test http-service -- test_videos_(unary 403, streaming 403, internal 500).


Summary by cubic
Fixes /v1/videos so failed video material URI downloads (HTTP 403, expired signatures, private buckets) surface as HTTP 403 with Validation metrics instead of HTTP 500/internal; genuine worker errors stay 500.
extract_http_like_errornow reads urllibHTTPError's.code/.msg(plus.reason) and never falls back tostr(exc), so signed URLs stay out of client bodies; 403 classifies asPermissionDenied.- Streaming
/v1/videoswaitsUntilFirstEventbefore committing SSE 200, so a first-event 403 answers HTTP 403 rather than 200. - The OSS SGLang video handler re-raises material-fetch 4xx so the frontend can map them, and a
PermissionDenieddiagnostic keeps the catalog message ("Permission denied") instead of the HTTP reason phrase.
Closes #14911.