{"record":{"id":"fdcbf67e025a7e4d","repo":"calesthio/OpenMontage","slug":"fal-key-or-fal-ai-api-key-required-for-image-uploa","errorCode":null,"errorMessage":"FAL_KEY or FAL_AI_API_KEY required for image upload","messagePattern":"FAL_KEY or FAL_AI_API_KEY required for image upload","errorType":"exception","errorClass":"RuntimeError","httpStatus":null,"severity":"error","filePath":"tools/video/_shared.py","lineNumber":424,"sourceCode":"                return video_url\n            raise RuntimeError(f\"Completed but no video_url in output: {data}\")\n\n        if status in {\"failed\", \"error\"}:\n            raise RuntimeError(f\"HeyGen generation failed: {data.get('error', 'Unknown')}\")\n\n        time.sleep(min(interval, max(0.0, deadline - time.time())))\n        interval = min(interval * 1.2, 30.0)\n\n    raise TimeoutError(f\"HeyGen execution {execution_id} timed out after {timeout}s\")\n\n\ndef upload_image_fal(image_path: str) -> str:\n    \"\"\"Upload a local image to fal.ai storage and return a public URL.\"\"\"\n    import requests\n\n    api_key = os.environ.get(\"FAL_KEY\") or os.environ.get(\"FAL_AI_API_KEY\")\n    if not api_key:\n        raise RuntimeError(\"FAL_KEY or FAL_AI_API_KEY required for image upload\")\n\n    path = Path(image_path)\n    if not path.exists():\n        raise FileNotFoundError(f\"Image not found: {image_path}\")\n\n    suffix = path.suffix.lower()\n    content_type = {\"png\": \"image/png\", \"jpg\": \"image/jpeg\", \"jpeg\": \"image/jpeg\", \"webp\": \"image/webp\"}.get(\n        suffix.lstrip(\".\"), \"image/png\"\n    )\n\n    # Initiate upload\n    init_resp = requests.post(\n        \"https://rest.alpha.fal.ai/storage/upload/initiate\",\n        headers={\"Authorization\": f\"Key {api_key}\", \"Content-Type\": \"application/json\"},\n        json={\"content_type\": content_type, \"file_name\": path.name},\n        timeout=30,\n    )\n    init_resp.raise_for_status()","sourceCodeStart":406,"sourceCodeEnd":442,"githubUrl":"https://github.com/calesthio/OpenMontage/blob/95e1c3d0ab93482159818560f6a8c8e866b9139f/tools/video/_shared.py#L406-L442","documentation":"Raised by upload_image_fal when neither FAL_KEY nor FAL_AI_API_KEY is set in the environment. Uploading a local image to fal.ai storage requires an authenticated key, so the function refuses before doing any I/O. The key is read only from these two environment variables.","triggerScenarios":"Running in a shell/process where the fal key was never exported; CI jobs with a sanitized env; .env file present but not loaded by the executing process; key named differently (e.g. FAL_API_KEY, which is NOT read).","commonSituations":"Local vs CI env drift; subprocess invocation that didn't inherit env; renaming conventions between FAL_KEY and FAL_AI_API_KEY across projects.","solutions":["export FAL_KEY=<key> (or FAL_AI_API_KEY) in the environment that runs the process","For CI, add the key as a masked secret and map it to FAL_KEY","If using a .env loader, verify it runs before tool invocation","Prefer uploading to HeyGen directly via upload_image_heygen when you already have a HeyGen key"],"exampleFix":"# before\n$ python make_video.py  # no key set\n# after\n$ export FAL_KEY=\"key-id:key-secret\"\n$ python make_video.py","handlingStrategy":"validation","validationCode":"import os\nif not (os.environ.get(\"FAL_KEY\") or os.environ.get(\"FAL_AI_API_KEY\")):\n    raise RuntimeError(\"set FAL_KEY before running uploads\")","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Use exactly FAL_KEY or FAL_AI_API_KEY (FAL_API_KEY is not read)","Add an env preflight at process start, not at upload time","In CI, inject the key as a masked secret mapped to FAL_KEY"],"tags":["fal","authentication","environment","credentials"],"backgroundTag":null,"analyzedSha":"95e1c3d0ab93482159818560f6a8c8e866b9139f","analyzedAt":"2026-08-15T06:31:20.014Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}