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

  1. Log the raw result payload to see what keys the endpoint actually returned
  2. Rephrase the prompt to avoid safety-filter rejections and retry
  3. Check fal.ai model changelog for the text_to_image endpoint and update parse_urls if the output schema changed
  4. 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

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


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)