{"record":{"id":"83305d5f6a87c4ea","repo":"Panniantong/Agent-Reach","slug":"audio-duration-exceeds-safety-limit-of-max-minute","errorCode":null,"errorMessage":"audio duration exceeds safety limit of {max_minutes} minutes","messagePattern":"audio duration exceeds safety limit of (.+?) minutes","errorType":"exception","errorClass":"TranscribeError","httpStatus":null,"severity":"error","filePath":"agent_reach/transcribe.py","lineNumber":149,"sourceCode":"    try:\n        duration = float(raw_duration)\n    except (TypeError, ValueError):\n        raise TranscribeError(\n            \"ffprobe could not parse a valid audio duration\"\n        ) from None\n    if not math.isfinite(duration) or duration <= 0:\n        raise TranscribeError(\n            \"ffprobe could not parse a valid positive audio duration\"\n        )\n    return duration\n\n\ndef _require_duration_within_budget(path: Path) -> float:\n    \"\"\"Reject audio that cannot fit within the bounded chunk budget.\"\"\"\n    duration = _probe_audio_duration(path)\n    if duration > MAX_AUDIO_SECONDS:\n        max_minutes = MAX_AUDIO_SECONDS // 60\n        raise TranscribeError(\n            f\"audio duration exceeds safety limit of {max_minutes} minutes\"\n        )\n    return duration\n\n\ndef _run(cmd: List[str], timeout: int = 600) -> None:\n    \"\"\"Run a subprocess, raising TranscribeError on nonzero exit or timeout.\n\n    cmd carries user-supplied URLs/paths into yt-dlp/ffmpeg — a stalled\n    network read or a hung probe must not block the CLI forever.\n    \"\"\"\n    try:\n        proc = subprocess.run(\n            cmd,\n            capture_output=True,\n            encoding=\"utf-8\",\n            errors=\"replace\",\n            timeout=timeout,","sourceCodeStart":131,"sourceCodeEnd":167,"githubUrl":"https://github.com/Panniantong/Agent-Reach/blob/93ae1d18c37b707dec053c7c4f9d91cd8ef8943d/agent_reach/transcribe.py#L131-L167","documentation":"Raised by _require_duration_within_budget (transcribe.py:144-152) when ffprobe reports an audio duration above MAX_AUDIO_SECONDS = 14400s (24 chunks x 600s = 240 minutes / 4 hours). The library enforces a bounded chunk budget so a single transcription job can never explode into unbounded API calls. The message renders the limit in minutes (240).","triggerScenarios":"transcribe(source) where source is a local file or downloaded media longer than 14400 seconds; _transcribe_in_dir calls _require_duration_within_budget(audio) at line 455 and ffprobe successfully parses duration > MAX_AUDIO_SECONDS. Typical with 4h+ podcast compilations, livestream recordings, or audiobook rips.","commonSituations":"Pointing the transcriber at long livestream VODs, multi-hour DJ sets, sleep-sound/white-noise videos, or accidentally passing a playlist-style compilation. Also hitting this right after a release that lowered MAX_CHUNKS/CHUNK_SECONDS constants.","solutions":["Trim the media to under 4 hours before calling transcribe(), e.g. ffmpeg -ss 0 -t 14390 -i input.m4a -c copy cut.m4a, and transcribe in parts","Verify the real duration first: ffprobe -v error -show_entries format=duration -of default=noprint_wrappers=1:nokey=1 file.m4a","If you genuinely need longer input, transcribe consecutive time windows (ffmpeg -ss/-t) and join the returned texts yourself","Only as a last resort, raise MAX_CHUNKS in your own fork — the limit also guards MAX_TOTAL_CHUNK_BYTES (96 MiB) and per-chunk 24 MiB API caps"],"exampleFix":"// before\ntranscribe(\"https://example.com/8h-livestream\")  # TranscribeError: 240 minutes\n\n// after: split into <4h windows\nfor i, ss in enumerate(range(0, 8*3600, 14_000)):\n    part = workdir / f\"part{i}.m4a\"\n    subprocess.run([\"ffmpeg\", \"-ss\", str(ss), \"-t\", \"14000\", \"-i\", src, \"-c\", \"copy\", str(part)], check=True)\n    text += transcribe(str(part), provider=\"groq\") + \"\\n\"","handlingStrategy":"validation","validationCode":"import subprocess\nfrom pathlib import Path\n\ndef fits_duration_budget(path: Path, max_seconds: int = 14400) -> bool:\n    out = subprocess.run(\n        [\"ffprobe\", \"-v\", \"error\", \"-show_entries\", \"format=duration\",\n         \"-of\", \"default=noprint_wrappers=1:nokey=1\", str(path)],\n        capture_output=True, text=True,\n    )\n    try:\n        return 0 < float(out.stdout.strip()) <= max_seconds\n    except ValueError:\n        return False","typeGuard":null,"tryCatchPattern":"from agent_reach.transcribe import TranscribeError\ntry:\n    text = transcribe(source)\nexcept TranscribeError as e:\n    if \"exceeds safety limit\" in str(e):\n        # split source into <4h windows yourself and transcribe each\n        ...\n    raise","preventionTips":["Probe duration with ffprobe before handing long-form media to transcribe()","Keep livestream/compilation sources pre-split into under-4-hour parts","Treat the 240-minute cap as a hard design constraint, not a tunable"],"tags":["audio","duration-limit","validation","transcription"],"backgroundTag":null,"analyzedSha":"93ae1d18c37b707dec053c7c4f9d91cd8ef8943d","analyzedAt":"2026-08-14T22:54:06.735Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}