affaan-m/ECC · error · FalError

fal call failed after one attempt; job acceptance may be…

Error message

fal call failed after one attempt; job acceptance may be unknown. Reconcile provider job status before requesting another generation.

What it means

falapi.submit() makes one attempt at a fal.ai job submission (fal_client call) and, on any exception, raises FalError noting that job acceptance is unknown. Provider details (which can embed keys, signed URLs, and payloads) are deliberately omitted and the exception is not chained (`from None`). Because acceptance is unknown, blindly retrying could duplicate a paid generation, so the message tells you to reconcile provider job status first.

Solutions

  1. Check fal.ai dashboard/logs for the job (or query provider job status) before retrying — do not blindly resubmit, or you may pay for two generations.
  2. Inspect the request payload and endpoint name for validity (correct endpoint ID, argument names, sizes within limits).
  3. Retry the call once network/provider health is confirmed; keep the attempt count at one per invocation as the library does.
  4. If failures persist, capture the request id/timestamp and check fal.ai status/incidents, then open a support thread; temporarily switch to --dry-run to unblock the pipeline.

Example fix

# before
try:
    mesh = text_to_3d(prompt)
except FalError:
    mesh = text_to_3d(prompt)  # risks duplicate paid job

# after
try:
    mesh = text_to_3d(prompt)
except FalError as e:
    if not provider_job_exists(job_id):  # reconcile before resubmitting
        mesh = text_to_3d(prompt)
    else:
        mesh = fetch_provider_result(job_id)
Defensive patterns

Strategy: retry

Validate before calling

# validate inputs before submit
assert endpoint and endpoint.startswith("fal-ai/"), f"bad endpoint: {endpoint!r}"
assert isinstance(payload, dict) and payload, "payload must be a non-empty dict"

Try / catch

try:
    result = submit(endpoint, payload)
except FalError:
    # acceptance unknown: check provider job status before resubmitting
    if job_was_accepted(job_id):
        result = await_provider_result(job_id)
    else:
        result = submit(endpoint, payload)  # safe resubmit

Prevention

When it happens

Trigger: Any of vlm_describe, image_to_3d, text_to_3d, retopologize, split_parts, or images_to_video calling submit() when the underlying fal_client.submit call raises — network failure, provider 4xx/5xx, timeout, malformed payload, or interrupted request where the job may or may not have been accepted.

Common situations: Transient network drop or DNS failure mid-submit; invalid endpoint name after an API version change; payload exceeding provider limits; expired/invalid FAL_KEY causing 401; hitting a provider rate limit; server-side 500 at fal.ai; the call being killed after the request was sent but before the response arrived (the 'acceptance unknown' case).

Understand the failure class

Background: "API request failed": what wrapped HTTP errors from external APIs mean and how to find the real cause — this error's family across 29 libraries.

Related errors


AI-assisted analysis of affaan-m/ECC@8321021c54 (2026-09-16). Data as JSON: /api/errors/d618cae8b0d32688. Report an issue: GitHub.

Appendix: source

Thrown at skills/taste-application/scripts/taste/falapi.py:270

    """Submit once. Ambiguous failures must be reconciled before another job.

    ``max_attempts`` is retained for call compatibility but never resubmits.
    """
    if is_dry_run():
        log.info("[dry-run] model request (payload omitted)")
        return _stub(endpoint, payload)

    require_live()
    api_key()
    try:
        result = _fal().subscribe(
            endpoint, arguments=payload, with_logs=False, client_timeout=timeout,
        )
        return result if isinstance(result, dict) else {"output": result}
    except Exception:
        # Exception strings can include keys, signed URLs and provider payloads.
        # Do not print or chain them into caller tracebacks.
        raise FalError(
            "fal call failed after one attempt; job acceptance may be unknown. "
            "Reconcile provider job status before requesting another generation."
        ) from None


# ---------------------------------------------------------------------------
# uploads (cached)
# ---------------------------------------------------------------------------

_UPLOAD_CACHE: dict[tuple[str, int, int], str] = {}
_UPLOAD_LOCK = threading.Lock()


def _cache_key(path: Path) -> tuple[str, int, int]:
    st = path.stat()
    return (str(path.resolve()), st.st_mtime_ns, st.st_size)

View on GitHub (pinned to 8321021c54)