{"record":{"id":"b38586239b08bc26","repo":"ATH-MaaS/Pixelle-Video","slug":"no-audio-file-generated-by-workflow","errorCode":null,"errorMessage":"No audio file generated by workflow","messagePattern":"No audio file generated by workflow","errorType":"exception","errorClass":"Exception","httpStatus":null,"severity":"error","filePath":"pixelle_video/services/tts_service.py","lineNumber":292,"sourceCode":"                logger.debug(f\"✅ Found audio in result.files: {audio_path}\")\n            # Check in outputs dictionary\n            elif hasattr(result, 'outputs') and result.outputs:\n                logger.debug(f\"Searching for audio file in result.outputs: {result.outputs}\")\n                # Try to find audio file in outputs\n                for key, value in result.outputs.items():\n                    if isinstance(value, str) and any(value.endswith(ext) for ext in ['.mp3', '.wav', '.flac']):\n                        audio_path = value\n                        logger.debug(f\"✅ Found audio in result.outputs[{key}]: {audio_path}\")\n                        break\n            \n            if not audio_path:\n                logger.error(\"No audio file generated\")\n                logger.error(f\"❌ Result analysis:\")\n                logger.error(f\"   - result.audios: {getattr(result, 'audios', 'NOT_FOUND')}\")\n                logger.error(f\"   - result.files: {getattr(result, 'files', 'NOT_FOUND')}\")\n                logger.error(f\"   - result.outputs: {getattr(result, 'outputs', 'NOT_FOUND')}\")\n                logger.error(f\"   - Full __dict__: {result.__dict__}\")\n                raise Exception(\"No audio file generated by workflow\")\n            \n            # If output_path provided and audio_path is URL, download to local\n            if output_path and audio_path.startswith(('http://', 'https://')):\n                import httpx\n                import os\n                \n                # Ensure parent directory exists\n                os.makedirs(os.path.dirname(output_path), exist_ok=True)\n                \n                logger.info(f\"Downloading audio from {audio_path} to {output_path}\")\n                async with httpx.AsyncClient() as client:\n                    response = await client.get(audio_path)\n                    response.raise_for_status()\n                    \n                    with open(output_path, 'wb') as f:\n                        f.write(response.content)\n                \n                logger.info(f\"✅ Generated audio (ComfyUI): {output_path}\")","sourceCodeStart":274,"sourceCodeEnd":310,"githubUrl":"https://github.com/ATH-MaaS/Pixelle-Video/blob/848b054e4fae40dabc62ec58e960b573e83793ac/pixelle_video/services/tts_service.py#L274-L310","documentation":"Even when the TTS workflow completes, _call_comfyui_workflow must find an audio file. It probes result.audios, then result.files, then scans result.outputs for strings ending in .mp3/.wav/.flac. If none yields a path it raises 'No audio file generated by workflow' after dumping the full result to logs. This indicates a completed run with no recognizable audio output.","triggerScenarios":"result.status == 'completed' but result.audios, result.files are empty/missing and result.outputs contains no string ending in .mp3/.wav/.flac — e.g. the workflow saves audio under a different extension (.ogg, .opus), the audio save node is bypassed, or outputs are nested dicts rather than plain strings.","commonSituations":"TTS node writes non-standard formats the extension scan misses; ComfyKit version whose result schema stores audio elsewhere (e.g. result.outputs['audio'] as a dict with 'url' key); RunningHub workflow without an exposed audio output node; SaveAudio node disabled in the graph.","solutions":["Check the logged 'Result analysis' block (result.audios/files/outputs and full __dict__) to see exactly where the audio landed; adapt extraction to that shape.","Ensure the workflow has an enabled audio output node and that RunningHub exposes it as an output file.","If the audio is saved with another extension (.ogg, .opus), extend the outputs scan's extension list or normalize the workflow to emit .mp3/.wav.","If outputs nest the URL inside a dict, update extraction to unwrap the value (e.g. value['url']) instead of requiring a bare string.","Upgrade ComfyKit if your version predates first-class audios support in its result object."],"exampleFix":"// before\nfor key, value in result.outputs.items():\n    if isinstance(value, str) and any(value.endswith(ext) for ext in ['.mp3', '.wav', '.flac']):\n        audio_path = value\n// after\nAUDIO_EXTS = ('.mp3', '.wav', '.flac', '.ogg', '.opus')\nfor key, value in result.outputs.items():\n    if isinstance(value, dict):\n        value = value.get('url') or value.get('path')\n    if isinstance(value, str) and value.endswith(AUDIO_EXTS):\n        audio_path = value\n        break","handlingStrategy":"type-guard","validationCode":"# pre-flight: workflow must have an enabled audio output node\ndef workflow_produces_audio(workflow_json: dict) -> bool:\n    nodes = workflow_json.get(\"nodes\", [])\n    return any(\"SaveAudio\" in n.get(\"type\", \"\") or \"AudioCombine\" in n.get(\"type\", \"\")\n               for n in nodes if not n.get(\"mode\"))\nassert workflow_produces_audio(load_workflow(tts_workflow_key))","typeGuard":"def extract_audio_path(result) -> str | None:\n    audios = getattr(result, \"audios\", None)\n    if audios:\n        return audios[0]\n    files = getattr(result, \"files\", None)\n    if files:\n        return files[0]\n    for v in (getattr(result, \"outputs\", None) or {}).values():\n        if isinstance(v, dict):\n            v = v.get(\"url\") or v.get(\"path\")\n        if isinstance(v, str) and v.endswith((\".mp3\", \".wav\", \".flac\", \".ogg\", \".opus\")):\n            return v\n    return None","tryCatchPattern":"try:\n    audio = await tts_service(...)\nexcept Exception as e:\n    if \"No audio file generated\" in str(e):\n        logger.error(\"TTS workflow produced no audio; inspect output node config and result schema\")\n        raise WorkflowConfigError(\"TTS workflow has no recognizable audio output\") from e\n    raise","preventionTips":["Keep an enabled audio save node in every TTS workflow and expose it on RunningHub.","Standardize output extensions to .mp3/.wav/.flac, or widen extraction to cover .ogg/.opus.","After ComfyKit upgrades, verify where audio appears in the result object (schema drift).","Log result.__dict__ on empty outputs — the service already dumps it; alert on that log line.","Smoke-test TTS workflows in CI so output-node regressions are caught before production."],"tags":["workflow-output","tts","comfyui","missing-output","audio"],"backgroundTag":"empty-workflow-output","analyzedSha":"848b054e4fae40dabc62ec58e960b573e83793ac","analyzedAt":"2026-08-30T03:24:41.468Z","schemaVersion":2},"datasetVersion":"2026-08-30T08:17:16.595Z"}