{"record":{"id":"ce7ef28ce156ec73","repo":"calesthio/OpenMontage","slug":"duration-seconds-is-required-for-cost-estimation","errorCode":null,"errorMessage":"duration_seconds is required for cost estimation","messagePattern":"duration_seconds is required for cost estimation","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"tools/audio/fal_elevenlabs_music.py","lineNumber":131,"sourceCode":"        \"Listen to the generated music for mood, mix, and duration\",\n    ]\n\n    _MODEL = \"fal-ai/elevenlabs/music\"\n    _QUEUE_URL = f\"https://queue.fal.run/{_MODEL}\"\n    _POLL_INTERVAL_SECONDS = 5\n    _MAX_WAIT_SECONDS = 600\n\n    def _get_api_key(self) -> str | None:\n        return os.environ.get(\"FAL_KEY\") or os.environ.get(\"FAL_AI_API_KEY\")\n\n    def get_status(self) -> ToolStatus:\n        return ToolStatus.AVAILABLE if self._get_api_key() else ToolStatus.UNAVAILABLE\n\n    def estimate_cost(self, inputs: dict[str, Any]) -> float:\n        \"\"\"fal bills $0.80 per output minute, rounded up to a full minute.\"\"\"\n        duration = inputs.get(\"duration_seconds\")\n        if duration is None:\n            raise ValueError(\"duration_seconds is required for cost estimation\")\n        return round(math.ceil(float(duration) / 60.0) * 0.80, 2)\n\n    def execute(self, inputs: dict[str, Any]) -> ToolResult:\n        api_key = self._get_api_key()\n        if not api_key:\n            return ToolResult(\n                success=False,\n                error=\"No fal.ai API key found. \" + self.install_instructions,\n            )\n\n        duration = inputs.get(\"duration_seconds\")\n        if duration is None:\n            return ToolResult(success=False, error=\"duration_seconds is required\")\n        duration = float(duration)\n        if not 3 <= duration <= 600:\n            return ToolResult(\n                success=False,\n                error=\"duration_seconds must be between 3 and 600\",","sourceCodeStart":113,"sourceCodeEnd":149,"githubUrl":"https://github.com/calesthio/OpenMontage/blob/95e1c3d0ab93482159818560f6a8c8e866b9139f/tools/audio/fal_elevenlabs_music.py#L113-L149","documentation":"Raised by fal_elevenlabs_music.estimate_cost when inputs has no duration_seconds. Because fal bills per output minute ($0.80/min rounded up), cost cannot be computed without a duration; the tool deliberately refuses to guess instead of defaulting silently.","triggerScenarios":"Calling estimate_cost (directly or via a cost-checking planner) with inputs lacking duration_seconds; execute() later enforces the same requirement.","commonSituations":"A pipeline/orchestrator calls estimate_cost before assembling full inputs; dict key typo (duration, length) instead of duration_seconds; porting code from a tool that defaulted the duration.","solutions":["Pass duration_seconds (number of seconds) in inputs before any cost estimation","Derive it from the approved target runtime in the script/proposal rather than a constant","Add a schema/validation check upstream that rejects inputs without duration_seconds early"],"exampleFix":"// before\ninputs = {\"prompt\": \"upbeat jingle\"}\ncost = tool.estimate_cost(inputs)  # raises\n\n// after\ninputs = {\"prompt\": \"upbeat jingle\", \"duration_seconds\": 30}\ncost = tool.estimate_cost(inputs)","handlingStrategy":"validation","validationCode":"if \"duration_seconds\" not in inputs or inputs[\"duration_seconds\"] is None:\n    raise ValueError(\"duration_seconds required before estimating fal music cost\")","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Assemble complete inputs (including duration) before any estimate_cost call","Derive duration from the approved runtime, never a hardcoded default","Centralize input schema checks in the orchestrator"],"tags":["fal","music","cost-estimation","validation"],"backgroundTag":null,"analyzedSha":"95e1c3d0ab93482159818560f6a8c8e866b9139f","analyzedAt":"2026-08-15T06:31:20.014Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}