fix(frontend): map video material HTTP 403 to 4xx (#14911)

PR
5d ago
+418468-81627

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/dynamo main.
  • Fixed rust-clippy (lib/bindings/python): clippy::needless_borrow in extract_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 new material-unavailable class.

Validation

  • cargo clippy --no-deps --all-targets -- -D warnings in lib/bindings/python (the previously failing job).
  • cargo test --lib http_like and http_statuses_map_to_semantic_classes in lib/bindings/python.
  • cargo test -p dynamo-llm --test http-service -- test_videos_ (unary 403, streaming 403, internal 500).
Open in Web Open in Cursor 

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_error now reads urllib HTTPError's .code/.msg (plus .reason) and never falls back to str(exc), so signed URLs stay out of client bodies; 403 classifies as PermissionDenied.
  • Streaming /v1/videos waits UntilFirstEvent before 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 PermissionDenied diagnostic keeps the catalog message ("Permission denied") instead of the HTTP reason phrase.

Closes #14911.