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
- Create a disposable Python 3.12 environment and install
livekit-agents==1.8.4 and livekit-plugins-runway==1.8.4.
- 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())
- Observe
max_retry=0 print http_attempts: 0 and the startup error. The max_retry=1 control prints http_attempts: 1 with no error.
- 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.
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. Inlivekit-plugins-runway1.8.4,_create_sessioniterates overrange(max_retry). With zero retries, it skips the request and raisesAPIConnectionError("Failed to start Runway Avatar Session after all retries")even when a local transport is ready to return success.Steps to reproduce
livekit-agents==1.8.4andlivekit-plugins-runway==1.8.4.max_retry=0printhttp_attempts: 0and the startup error. Themax_retry=1control printshttp_attempts: 1with no error.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 sharedAPIConnectOptionscontract.Runtime or environment
Operating system: macOS arm64. Python 3.12.13;
livekit-plugins-runway1.8.4;livekit-agents1.8.4;livekit1.1.20;livekit-api1.2.1;aiohttp3.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 is96341b0db2d0224403a36612cb04a1e38d60d405.Evidence
APIConnectOptionsdocumentation and validation describemax_retryas the maximum number of retries and permit zero.range(self._conn_options.max_retry)before its final error.startpath reproduced zero HTTP attempts formax_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 mostNretries. Session, room, and call IDs are unnecessary because all reproduction objects are local.