Surfaced by the Codex adversarial security audit on PR #35. One of 4 deferred MEDIUMs.
Where (two spots)
Spot A — backend/app/ml/frame_relight.py:71-78:
for part in response.parts or []:
inline = getattr(part, "inline_data", None)
if inline and getattr(inline, "data", None):
with open(out_path, "wb") as f:
f.write(inline.data)
return out_path
raise RuntimeError("Nano Banana returned no image (likely content policy)")
Spot B — backend/app/restyle/pipeline.py:129-132:
except Exception as exc:
job = _ensure_job(jobs, job_id)
job["status"] = "failed"
job["logs"].append(f"❌ {exc}")
What's wrong
A: Content-policy refusals from gemini-3.1-flash-image-preview come back as a response with no inline_data parts. We infer the refusal from the absence — but the google-genai SDK actually raises typed exceptions (google.genai.errors.ClientError, google.genai.errors.ServerError) on hard failures, and surfaces response.prompt_feedback.block_reason on soft policy hits. The current code conflates "hard API error", "network timeout", "safety block", and "empty result" under one generic RuntimeError.
B: The pipeline's outer except Exception catches everything from a ValueError(\"video too long\") to httpx.ConnectError to a programmer mistake. Users see "❌ {exc}" with whatever message the framework happened to throw. No structured error class on the response.
Severity
MEDIUM. Doesn't expose security info, but it makes incidents painful to debug and produces a poor UX (policy refusal vs network blip look identical to the user).
Suggested fix
-
In frame_relight.py:
from google.genai import errors as genai_errors
try:
response = client.models.generate_content(...)
except genai_errors.ClientError as e:
raise ContentPolicyError(...) # new typed class
except genai_errors.ServerError as e:
raise ProviderError(...) from e
if response.prompt_feedback and response.prompt_feedback.block_reason:
raise ContentPolicyError(reason=response.prompt_feedback.block_reason)
-
In restyle/pipeline.py, catch typed errors explicitly and surface user-facing messages keyed by error class:
ContentPolicyError → "This style request was rejected by Gemini's safety policy. Try a different background/lighting prompt."
FalError → "The video model encountered an error. Try again in a moment."
FileNotFoundError / OSError → log internally, generic message externally.
- Bare
Exception → keep as last-resort log; mark status=\"failed\" but with a generic user message.
References
Surfaced by the Codex adversarial security audit on PR #35. One of 4 deferred MEDIUMs.
Where (two spots)
Spot A —
backend/app/ml/frame_relight.py:71-78:Spot B —
backend/app/restyle/pipeline.py:129-132:What's wrong
A: Content-policy refusals from
gemini-3.1-flash-image-previewcome back as a response with noinline_dataparts. We infer the refusal from the absence — but thegoogle-genaiSDK actually raises typed exceptions (google.genai.errors.ClientError,google.genai.errors.ServerError) on hard failures, and surfacesresponse.prompt_feedback.block_reasonon soft policy hits. The current code conflates "hard API error", "network timeout", "safety block", and "empty result" under one genericRuntimeError.B: The pipeline's outer
except Exceptioncatches everything from aValueError(\"video too long\")tohttpx.ConnectErrorto a programmer mistake. Users see "❌ {exc}" with whatever message the framework happened to throw. No structured error class on the response.Severity
MEDIUM. Doesn't expose security info, but it makes incidents painful to debug and produces a poor UX (policy refusal vs network blip look identical to the user).
Suggested fix
In
frame_relight.py:In
restyle/pipeline.py, catch typed errors explicitly and surface user-facing messages keyed by error class:ContentPolicyError→ "This style request was rejected by Gemini's safety policy. Try a different background/lighting prompt."FalError→ "The video model encountered an error. Try again in a moment."FileNotFoundError/OSError→ log internally, generic message externally.Exception→ keep as last-resort log; markstatus=\"failed\"but with a generic user message.References
google-genaierror hierarchy: https://googleapis.github.io/python-genai/genai.html#module-google.genai.errors