calesthio/OpenMontage · error · RuntimeError

Doubao task completed but did not return data.audio_url

Error message

Doubao task completed but did not return data.audio_url

What it means

Raised when the Doubao async TTS polling loop reports task_status == 2 (success) but the returned data object contains no audio_url. The wrapper refuses to continue because the next step downloads the audio from that URL. The literal-brace message never interpolates, so the actual payload is invisible unless you instrument it.

Source

Thrown at tools/audio/doubao_tts.py:256

        self._raise_for_doubao_error(submit_response.status_code, submit_data)

        task_id = submit_data.get("data", {}).get("task_id")
        if not task_id:
            raise RuntimeError("Doubao submit succeeded but did not return data.task_id")

        query_data = self._poll_query(
            requests_module=requests,
            api_key=api_key,
            resource_id=resource_id,
            task_id=task_id,
            return_usage=bool(inputs.get("return_usage", True)),
            poll_interval=float(inputs.get("poll_interval_seconds", 2.0)),
            timeout_seconds=int(inputs.get("timeout_seconds", 300)),
        )
        data = query_data.get("data", {})
        audio_url = data.get("audio_url")
        if not audio_url:
            raise RuntimeError("Doubao task completed but did not return data.audio_url")

        audio_response = requests.get(audio_url, timeout=(10, 120))
        audio_response.raise_for_status()
        output_path.write_bytes(audio_response.content)
        metadata_path.write_text(json.dumps(query_data, ensure_ascii=False, indent=2) + "\n", encoding="utf-8")

        audio_duration = self._audio_duration(output_path)
        usage = data.get("usage")
        cost = self._cost_from_usage(usage) or self.estimate_cost(inputs)

        return ToolResult(
            success=True,
            data={
                "provider": self.provider,
                "model": resource_id,
                "resource_id": resource_id,
                "voice_id": voice_id,
                "format": fmt,

View on GitHub (pinned to 95e1c3d0ab)

Solutions

  1. Persist query_data to a debug log and inspect which keys data actually carries (sometimes the URL moved to data.audio.url or similar)
  2. Retry the whole submit — a fresh task usually produces a fresh audio_url
  3. Shorten the gap between completion and download by lowering poll_interval_seconds
  4. Confirm the resource_id/endpoint pairing matches the current Volcengine console
Defensive patterns

Strategy: retry

Try / catch

for attempt in range(2):
    try:
        result = doubao_tool.execute(inputs)
        break
    except RuntimeError as e:
        if "audio_url" in str(e) and attempt == 0:
            continue  # re-submit: completed task lost its artifact
        raise

Prevention

When it happens

Trigger: QUERY_URL returns status 2 with data.audio_url missing/null/empty — expired or purged audio, partial server-side success, or an envelope change in the query response.

Common situations: Long polls where the audio expired between completion and query; region/console migration altering the response schema; account state (quota exhausted mid-task) producing a 'succeeded' status with no artifact.

Related errors


AI-assisted analysis of calesthio/OpenMontage@95e1c3d0ab (2026-08-15). Data as JSON: /api/errors/75f1674af9ae8d68. Report an issue: GitHub.