Skip to content

PC-4990: end the CAS exchange after an exchange error - #1964

Draft
al3xanndru wants to merge 1 commit into
mainfrom
wabbit-dri/PC-4990_1
Draft

al3xanndru wants to merge 1 commit into
mainfrom
wabbit-dri/PC-4990_1

Conversation

@al3xanndru

@al3xanndru al3xanndru commented Oct 30, 2026 •

Copy link
Copy Markdown
Contributor

Problem

PC-4990: when a conversational low-code agent hits its max-iterations limit in Studio Web's debug chat, the chat shows "Maximum number of sequential steps reached. You may send a new message to tell the agent to continue." But the input is still in its generating state. "Generating…" is still shown and the send button is still the Stop square, so the user can't send the message the error asks for. Re-checked on cloud on Oct 29: it still reproduces (maxIterations = 3, one tool, prompt asks for ten sequential calls).

Root cause

When a run faults, UiPathChatRuntime (uipath-runtime) calls chat_bridge.emit_exchange_error_event(error) and stops there. Only the successful branch calls emit_exchange_end_event(). SocketIOChatBridge.emit_exchange_error_event sends only the exchangeError event, so CAS never gets endExchange for a faulted exchange.

The CAS chat client (UiPath/AgentInterfaces, lib/react-sdk/src/providers/ConversationProvider.tsx) clears the generating state only in exchange.onExchangeEnd: chat.stopResponse(), setShowLoading(false), setWaiting(false). An exchangeError start only adds the error banner (onAnyErrorStart → chat.setError). The CAS cloud service relays agent events and does not end an exchange on an error. So every faulted URT conversational run leaves the UI on Stop, not just max iterations. Max iterations is the case users hit most, and its message tells them to send another message.

The max-iterations check (uipath_langchain/agent/react/llm_node.py) runs before the LLM call. Every earlier assistant message and tool call is already closed, so the missing endExchange is the only thing that keeps the UI stuck.

Change

packages/uipath/src/uipath/_cli/_chat/_bridge.py:

  • emit_exchange_error_event now calls emit_exchange_end_event() after it sends the error. The error is terminal for the exchange: low-code jobs end, and coded jobs fault.
  • emit_exchange_end_event still honours end_exchange=False, so the exchange stays open for a downstream consumer.
  • emit_exchange_end_event now sends the event at most once per bridge (that is, per exchange). A failure raised after the exchange already ended (the runtime's outer except path) does not send a second endExchange. The flag is set only after a successful send, so a failed send can still be retried.
  • The exchange is not ended if sending the error itself fails. The RuntimeError is raised as before.
  • Version bump: uipath 2.14.44 → 2.14.45.

Tests

packages/uipath/tests/cli/chat/test_bridge.py, new class TestSocketIOChatBridgeErrorEndsExchange:

  • A max-iterations error sends exchangeError (AGENT_MAXIMUM_SEQUENTIAL_STEPS_REACHED with the Temporal message) and then endExchange, checked against the exact payloads.
  • With end_exchange=False, only the error is sent.
  • If the exchange already ended, a later error does not end it again.
  • If the error send fails, no endExchange is sent.
  • A failed endExchange can be retried by a later error.
  • End to end through the real UiPathChatRuntime (uipath-runtime 0.13.5): a delegate that yields a FAULTED TERMINATION_MAX_ITERATIONS result produces exchangeError and then endExchange.
  • Updated test_emit_exchange_error_event_sends_when_connected: it now expects 2 emits.

Results:

  • pytest tests/cli/chat: 119 passed. Against the unfixed bridge, 6 of the new or updated tests fail (3 cases × asyncio/trio).
  • ruff check . and ruff format --check . are clean, as is scripts/lint_httpx_client.py.
  • mypy src/uipath/_cli/_chat tests/cli/chat: no issues.
  • Full packages/uipath suite: see the summary in the draft record.

Risks / open questions

  • Behaviour change for every error, not just max iterations. Any faulted URT conversational exchange (licensing, incomplete response, invalid input, unexpected error) now also ends the exchange. The UI goes back to Send with the error banner still shown. That matches what Stop already does from the client (onClickStopResponse ends the exchange itself), and the job is ending anyway. CAS owners should confirm that endExchange after exchangeError is the expected agent-side protocol. The SDK's ExchangeEventHelper allows it: sendErrorStart does not end the exchange.
  • Where the fix belongs. You could argue the fix belongs in uipath-runtime-python's UiPathChatRuntime FAULTED branch, next to the success path's emit_exchange_end_event(). It is in the bridge because that is the CAS-specific layer that already owns exchange-end policy (end_exchange). The once-only guard keeps it correct if the runtime later adds its own end call.
  • The .NET/Temporal conversational engine (Agents/backend/Execution.Engines.Conversational.V1/ConversationalEngineWorkflow.cs) also sends only SendExchangeError on max steps. It is not changed here: the reported runs are URT (configurable maxIterations, AGENT_RUNTIME.TERMINATION_MAX_ITERATIONS).
  • To ship: low-code conversational agents get this when the URT image picks up uipath ≥ 2.14.45.

Drafted by the wabbit-dri draft-fixes loop for PC-4990 (draft #1), for a human to review: not ready to merge as is.

🤖 Generated with Claude Code

A faulted conversational run (e.g. max iterations) sent exchangeError but
never endExchange. The CAS chat UI leaves its generating state (Stop
button, "Generating...") only on endExchange, so the user could not send
the message the max-steps error tells them to send.

emit_exchange_error_event now ends the exchange after the error, honouring
end_exchange=False; emit_exchange_end_event is sent at most once.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@github-actions github-actions Bot added test:uipath-langchain Triggers tests in the uipath-langchain-python repository test:uipath-runtime test:uipath-integrations labels Oct 30, 2026
@sonarqubecloud

Copy link
Copy Markdown

This branch has not been deployed

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

Labels

test:uipath-integrations test:uipath-langchain Triggers tests in the uipath-langchain-python repository test:uipath-runtime

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant