Skip to content

"FleetApi.start()" uses default 30s timeout but "fleet.start" RPC blocks until fleet completes #539

Description

@Rasaboun

Summary

session.rpc.fleet.start() is unusable for any non-trivial workload because it inherits the 30s default timeout from JsonRpcClient.request(), but the session.fleet.start RPC is a long-running blocking call that only responds once the fleet finishes and the session goes idle.

The problem

FleetApi.start() is a thin wrapper with no timeout parameter:

# copilot/generated/rpc.py
class FleetApi:
    async def start(self, params: SessionFleetStartParams) -> SessionFleetStartResult:
        params_dict = {k: v for k, v in params.to_dict().items() if v is not None}
        params_dict["sessionId"] = self._session_id
        return SessionFleetStartResult.from_dict(
            await self._client.request("session.fleet.start", params_dict)
        )

It calls self._client.request(...) which defaults to 30s:

# copilot/jsonrpc.py
async def request(self, method, params=None, timeout=30.0):
    ...
    return await asyncio.wait_for(future, timeout=timeout)

Any fleet workload taking more than 30s raises asyncio.TimeoutError.

Observed behavior

I ran 5 tests against github-copilot-sdk==0.1.26rc0 / @github/copilot@0.0.411 to characterize fleet.start:

Test Result
Trivial plan (1 task) fleet.start returned after 47.6s — same time as SESSION_IDLE
No plan at all Returned after 8.3s with started=True (agent had nothing to do)
Typed API, trivial plan Returned after 14.7s — happened to fit within 30s
notify instead of request Fleet did not run at all — must be a request
Precise ordering (2 tasks) FLEET_RETURNED and SESSION_IDLE at exactly the same time (23.04s)

Key findings

  1. fleet.start blocks until the fleet finishes — it returns at the same time as SESSION_IDLE, not when the fleet is "started"
  2. notify does not workfleet.start must be called as a JSON-RPC request
  3. started=True is always returned (even with no plan), so the field doesn't convey meaningful status
  4. The 30s default timeout causes TimeoutError for any real workload — forcing users to bypass the typed API

Current workaround

Bypass FleetApi.start() and call the raw RPC with a longer timeout:

params = SessionFleetStartParams(prompt="implement the plan")
params_dict = {k: v for k, v in params.to_dict().items() if v is not None}
params_dict["sessionId"] = session.session_id

raw_result = await session._client.request(
    "session.fleet.start", params_dict, timeout=600.0
)
result = SessionFleetStartResult.from_dict(raw_result)

Reproduction

import asyncio
from copilot import CopilotClient
from copilot.generated.rpc import Mode, SessionFleetStartParams, SessionModeSetParams

async def main():
    client = CopilotClient()
    await client.start()
    session = await client.create_session(
        {"model": "gpt-5-mini", "streaming": "true", "reasoning_effort": "low"}
    )

    await session.rpc.mode.set(SessionModeSetParams(mode=Mode.PLAN))
    await session.send_and_wait(
        {"prompt": "Create a plan with 4 independent tasks: write factorial.py, palindrome.py, fibonacci.py, bubblesort.py"},
        timeout=120.0,
    )

    await session.rpc.mode.set(SessionModeSetParams(mode=Mode.AUTOPILOT))

    # This will raise asyncio.TimeoutError for any plan that takes >30s
    result = await session.rpc.fleet.start(
        SessionFleetStartParams(prompt="implement the plan")
    )

asyncio.run(main())

Suggested fix

Make fleet.start return immediately with an acknowledgment (started=True) and let the caller wait via SESSION_IDLE events. This would match what the return type name (started) implies.

Environment

  • github-copilot-sdk: 0.1.26rc0
  • @github/copilot CLI: 0.0.411
  • Python: 3.12
  • macOS Darwin 25.2.0

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

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions