affaan-m/ECC · error · FalError
no image URL in response from
Error message
no image URL in response from {ENDPOINTS['text_to_image']} What it means
FalError raised by text_to_image() after submit() returns a result from which parse_urls() extracts no image URLs. It names the failing endpoint so you know which model's response was malformed. One image is requested (num_images=1), so an empty URL list means the provider did not return the expected output.
Solutions
- Log the raw result dict to inspect what the endpoint actually returned
- Rephrase the prompt if a content policy rejection is suspected and retry
- Check the fal.ai model page for the endpoint's current response schema and update parsing
- Wrap submit/parse in a retry for transient provider failures
Example fix
// before
urls = falapi.text_to_image(prompt) # FalError: no image URL in response
// after
try:
urls = falapi.text_to_image(prompt)
except falapi.FalError:
result = falapi.submit(falapi.ENDPOINTS["text_to_image"], {"prompt": prompt, "num_images": 1})
raise RuntimeError(f"text_to_image returned no URLs; keys={list(result) if isinstance(result, dict) else type(result)}") Defensive patterns
Strategy: retry
Validate before calling
result = falapi.submit(falapi.ENDPOINTS["text_to_image"], {"prompt": prompt, "num_images": 1})
assert falapi.parse_urls(result), f"no image URLs; keys={list(result) if isinstance(result, dict) else type(result)}" Try / catch
try:
urls = falapi.text_to_image(prompt)
except falapi.FalError:
prompt = sanitize_prompt(prompt) # may have tripped content policy
urls = falapi.text_to_image(prompt) Prevention
- Log raw provider responses in dev to spot schema changes early
- Rephrase prompts that may trigger content filters
- Add a retry with backoff around text_to_image for transient provider issues
- Track the fal.ai model version; update parsing when the schema changes
When it happens
Trigger: text_to_image(prompt, ...) where the fal response lacks its image output field — provider response-shape change, content-policy rejection returning an error object, or model failure.
Common situations: Prompt tripped a provider safety filter and the endpoint returned no images; fal.ai updated the model's output schema; transient provider error surfaced as an empty result.
Understand the failure class
Background: "invalid response format", "malformed payload", "missing data field": when an API returns 200 but the response shape is wrong — this error's family across 23 libraries.
Related errors
- no URL in provider response; response shape may have…
- fal call failed after one attempt; job acceptance may be…
- fal call failed after one attempt; job acceptance may be…
- fal upload failed; provider details omitted
- fal upload failed; provider details omitted
AI-assisted analysis of affaan-m/ECC@8321021c54 (2026-09-16).
Data as JSON: /api/errors/61423a1bddf5c4cd.
Report an issue: GitHub.
Appendix: source
Thrown at skills/taste-distillation/scripts/taste/falapi.py:622
def quantize_duration(duration: float, lo: int = 3, hi: int = 12) -> int:
"""Round a planned shot length onto the video model's supported grid."""
return int(max(lo, min(hi, round(float(duration)))))
def text_to_image(
prompt: str,
image_refs: list[str] | None = None,
*,
timeout: int = 300,
) -> list[str]:
"""Generate stills, optionally conditioned on reference images."""
payload: dict[str, Any] = {"prompt": prompt, "num_images": 1}
if image_refs:
payload["image_urls"] = list(image_refs)
result = submit(ENDPOINTS["text_to_image"], payload, timeout)
urls = parse_urls(result)
if not urls:
raise FalError(f"no image URL in response from {ENDPOINTS['text_to_image']}")
return urls
def extract_frame(video_url: str, timestamp: float, *, timeout: int = 300) -> str:
"""Pull a single frame out of a hosted video. Returns the image URL."""
payload = {"video_url": video_url, "timestamp": round(float(timestamp), 3)}
result = submit(ENDPOINTS["extract_frame"], payload, timeout)
return first_url(result, ENDPOINTS["extract_frame"])
def compose(tracks: list[dict], *, timeout: int = 900) -> str:
"""Composite timeline tracks into one video. Returns the output URL.
``tracks`` is passed straight through so the caller owns the timeline
shape; the ffmpeg-api track schema is another default worth verifying
before a live run.
"""
result = submit(ENDPOINTS["compose"], {"tracks": tracks}, timeout)View on GitHub (pinned to 8321021c54)