Skip to content

fix(runway): make an initial session request when max_retry is zero #7604

Description

@coygeek

Summary

APIConnectOptions(max_retry=0) is valid and means zero retries, but the Runway avatar plugin performs zero initial HTTP attempts and fails session startup immediately. In livekit-plugins-runway 1.8.4, _create_session iterates over range(max_retry). With zero retries, it skips the request and raises APIConnectionError("Failed to start Runway Avatar Session after all retries") even when a local transport is ready to return success.

Steps to reproduce

  1. Create a disposable Python 3.12 environment and install livekit-agents==1.8.4 and livekit-plugins-runway==1.8.4.
  2. Run the following minimized local reproduction. The private method and HTTP stub isolate the session-creation retry loop; all strings are unused synthetic placeholders, and the stub performs no network request.
import asyncio
from livekit.agents import APIConnectOptions, APIConnectionError
from livekit.plugins.runway import AvatarSession

class Response:
    ok = True
    async def __aenter__(self): return self
    async def __aexit__(self, *args): pass
    async def json(self): return {"id": "local-session"}

class FakeHTTP:
    def __init__(self): self.attempts = 0
    def post(self, *args, **kwargs):
        self.attempts += 1
        return Response()

async def main():
    for retries in (0, 1):
        http = FakeHTTP()
        avatar = AvatarSession(
            preset_id="cat-character", api_key="unused-local-placeholder",
            conn_options=APIConnectOptions(max_retry=retries),
        )
        avatar._http_session = http
        avatar._local_participant_identity = "local-agent"
        error = None
        try:
            await avatar._create_session("wss://unused.invalid", "unused-token", "local-room")
        except APIConnectionError as exc:
            error = str(exc)
        print({"max_retry": retries, "http_attempts": http.attempts, "error": error})

asyncio.run(main())
  1. Observe max_retry=0 print http_attempts: 0 and the startup error. The max_retry=1 control prints http_attempts: 1 with no error.
  2. The same zero-attempt behavior also occurs through public AvatarSession.start() with a local fake room, fake agent session, fake job context, explicit synthetic LiveKit arguments, and the same HTTP stub. The public-start path was exercised separately so the result is not limited to direct private-method invocation.

Expected behavior

Zero retries still permits one initial HTTP attempt. A successful first response starts the session. A failed initial request is surfaced without another attempt when max_retry=0.

Actual behavior

No POST is attempted with max_retry=0, and startup fails immediately. Positive values also count total attempts rather than retries, so the loop is inconsistent with the documented retry count.

Affected area

livekit-plugins/livekit-plugins-runway/livekit/plugins/runway/avatar.py, specifically _create_session, and its use of the shared APIConnectOptions contract.

Runtime or environment

Operating system: macOS arm64. Python 3.12.13; livekit-plugins-runway 1.8.4; livekit-agents 1.8.4; livekit 1.1.20; livekit-api 1.2.1; aiohttp 3.14.3. Models used: none; the reproduction needs no STT, LLM, TTS, provider account, or live session. Both LiveKit packages remain the current PyPI releases, and the current repository revision is 96341b0db2d0224403a36612cb04a1e38d60d405.

Evidence

  • APIConnectOptions documentation and validation describe max_retry as the maximum number of retries and permit zero.
  • The Runway creation loop iterates range(self._conn_options.max_retry) before its final error.
  • Fresh executions of both the minimized creation loop and the public start path reproduced zero HTTP attempts for max_retry=0. The positive-count control reached the fake HTTP transport and succeeded.

Impact

Applications that disable retries cannot start a Runway avatar session, regardless of provider availability. The failure occurs before the initial request.

Additional context

A regression check should assert that zero retries makes exactly one attempt, that a successful initial response returns successfully, and that a retryable failure makes no additional attempt with zero retries. For a configured retry count N, the maximum attempt count should match the shared contract of one initial attempt plus at most N retries. Session, room, and call IDs are unnecessary because all reproduction objects are local.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions