{"record":{"id":"769f0dc0870d1e0b","repo":"affaan-m/ECC","slug":"no-url-in-provider-response-response-shape-may-have-changed-769f0d","errorCode":null,"errorMessage":"no URL in provider response; response shape may have changed (provider payload omitted)","messagePattern":"no URL in provider response; response shape may have changed \\(provider payload omitted\\)","errorType":"exception","errorClass":"FalError","httpStatus":null,"severity":"error","filePath":"skills/taste-distillation/scripts/taste/falapi.py","lineNumber":370,"sourceCode":"        elif isinstance(node, dict):\n            if isinstance(node.get(\"url\"), str):\n                found.append(node[\"url\"])\n            for k, v in node.items():\n                if k != \"url\":\n                    walk(v)\n        elif isinstance(node, (list, tuple)):\n            for v in node:\n                walk(v)\n\n    walk(result)\n    seen: set[str] = set()\n    return [u for u in found if not (u in seen or seen.add(u))]\n\n\ndef first_url(result: Any, endpoint: str) -> str:\n    urls = parse_urls(result)\n    if not urls:\n        raise FalError(\n            \"no URL in provider response; response shape may have changed \"\n            \"(provider payload omitted)\"\n        )\n    return urls[0]\n\n\ndef _mesh_url(result: Any, endpoint: str) -> str:\n    \"\"\"The GLB out of a 3D response, addressed by key rather than by position.\n\n    ``first_url`` would work only as long as ``model_glb`` happens to be the\n    first URL-bearing key in the response. It is today; the response also\n    carries a ``thumbnail`` PNG and a ``model_urls`` block with obj/fbx/mtl,\n    so a key reordering upstream would quietly start returning a preview image\n    where a mesh is expected - and a preview image downloads fine, so nothing\n    would fail until Blender refused to open it.\n    \"\"\"\n    if isinstance(result, dict):\n        for path in ((\"model_glb\", \"url\"), (\"model_urls\", \"glb\", \"url\"),","sourceCodeStart":352,"sourceCodeEnd":388,"githubUrl":"https://github.com/affaan-m/ECC/blob/8321021c54d670126ce3b2969d5deb880b4b0c2a/skills/taste-distillation/scripts/taste/falapi.py#L352-L388","documentation":"FalError raised by first_url() when parse_urls() finds no URL in a provider result. It guards against fal.ai response-shape changes: if the expected nested URL is missing, the library refuses to return a bogus value and notes the payload is omitted. Called by _mesh_url, retopologize, split_parts, images_to_video, reference_to_video, extract_frame.","triggerScenarios":"Any wrapper (e.g. extract_frame, merge_videos, text_to_3d) whose submit() result dict lacks the field where the output URL normally lives — provider API updated, model returned an error object instead of output, or the job produced no artifacts.","commonSituations":"fal.ai upgraded an endpoint and renamed the output key; the model failed silently and returned a partial/empty payload; passing a result from the wrong endpoint to first_url.","solutions":["Log the raw result dict (locally, not in shared output) to inspect the actual response shape","Update the parse/expected key to match the current provider response schema","Check fal.ai model page/changelog for the endpoint's current output format","Guard upstream: verify the generation job succeeded before extracting the URL"],"exampleFix":"// before\nurl = falapi.first_url(result, ENDPOINTS[\"compose\"])  # FalError: no URL in provider response\n// after\nurls = falapi.parse_urls(result)\nif not urls:\n    raise RuntimeError(f\"no URL from compose; result keys: {list(result) if isinstance(result, dict) else type(result)}\")\nurl = urls[0]","handlingStrategy":"try-catch","validationCode":"urls = falapi.parse_urls(result)\nif not urls:\n    raise RuntimeError(f\"no URL in result; type={type(result)}, keys={list(result) if isinstance(result, dict) else 'n/a'}\")","typeGuard":"def has_url(result) -> bool:\n    return bool(falapi.parse_urls(result))","tryCatchPattern":"try:\n    url = falapi.first_url(result, endpoint)\nexcept falapi.FalError:\n    dump_result_keys(result)  # inspect shape locally before deciding\n    raise","preventionTips":["Pin/check the fal.ai model version for each endpoint; watch changelogs for schema changes","Inspect raw result payloads in dev before trusting first_url","Validate generation succeeded before URL extraction","Keep a small smoke test per endpoint that asserts URL extraction works"],"tags":["python","api","response-shape","provider"],"backgroundTag":"unexpected-api-response-shape","analyzedSha":"8321021c54d670126ce3b2969d5deb880b4b0c2a","analyzedAt":"2026-09-16T10:08:13.343Z","contentChangedAt":"2026-09-16T10:08:13.343Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}