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
text_to_image submits a prompt to fal.ai's text-to-image endpoint and then extracts image URLs from the response. If parse_urls finds none, the library raises FalError naming the endpoint, since a successful text-to-image call must return at least one image URL.
Solutions
- Log the raw result payload to see what keys the endpoint actually returned
- Rephrase the prompt to avoid safety-filter rejections and retry
- Check fal.ai model changelog for the text_to_image endpoint and update parse_urls if the output schema changed
- Retry the request — transient provider failures can yield empty outputs
Example fix
# before
result = submit(ENDPOINTS['text_to_image'], payload, timeout)
urls = parse_urls(result)
# after
result = submit(ENDPOINTS['text_to_image'], payload, timeout)
print('t2i payload keys:', list(result.keys()) if isinstance(result, dict) else type(result))
urls = parse_urls(result) Defensive patterns
Strategy: try-catch
Validate before calling
if not prompt or not prompt.strip():
raise ValueError('prompt must be non-empty') Try / catch
try:
urls = text_to_image(prompt)
except FalError:
urls = text_to_image(sanitized_prompt) # rephrase to dodge safety filter Prevention
- Log the raw endpoint payload to diagnose empty outputs
- Avoid prompt content likely to trigger fal.ai's safety filters
- Retry once on empty responses before failing the pipeline
- Track fal.ai model changelogs for output schema changes
When it happens
Trigger: submit(ENDPOINTS['text_to_image'], ...) returns a payload without recognizable URL keys — e.g. the generation failed server-side while returning 200, a safety filter suppressed output, or the response schema changed for that endpoint.
Common situations: Prompt triggers fal.ai content moderation so no image is produced; fal.ai upgrades the model and renames the output field (e.g. images[].url); network/proxy layer returns a truncated or empty JSON body.
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/3facaf109d850869.
Report an issue: GitHub.
Appendix: source
Thrown at skills/taste-application/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)