Skip to content

feat: carry ConversationErrorEvent on ConversationRunError for automation callbacks - #4458

Draft
neubig wants to merge 3 commits into
mainfrom
feat/report-automation-blocking-factor
Draft

feat: carry ConversationErrorEvent on ConversationRunError for automation callbacks#4458
neubig wants to merge 3 commits into
mainfrom
feat/report-automation-blocking-factor

Conversation

@neubig

@neubig neubig commented Aug 11, 2026

Copy link
Copy Markdown
Member

Summary

Carries the SDK's existing typed ConversationErrorEvent on ConversationRunError so the workspace automation completion callback can serialize it directly. This is the SDK side of OpenHands/automation #325 / issue #323.

Changes

  • ConversationRunError.conversation_error: New optional field carrying the authoritative ConversationErrorEvent emitted during the failed run.
  • Local conversation: Both run() and arun() attach the latest ConversationErrorEvent emitted during the current run (scoped by _run_start_event_count).
  • Remote conversation: _handle_conversation_status retrieves the latest structured error from cached events and attaches it.
  • Workspace callback: _send_completion_callback serializes ConversationRunError.conversation_error directly as the callback error field. For exceptions outside a conversation (e.g. setup failures), it constructs a fallback ConversationErrorEvent that self-classifies via the existing classify_error.

Why this design

  • The conversation already owns the authoritative classified error event.
  • ConversationRunError already travels from conversation.run() through workspace.__exit__.
  • No mutable workspace registration, no callback-side reclassification, no new Pydantic models.
  • SDK users can inspect exc.conversation_error directly.
  • The callback error is now a structured ConversationErrorEvent (or a legacy string for older SDKs).

Validation

  • Focused tests: local/remote authoritative event attachment, fallback event creation, workspace callback serialization, existing cost/conversation_id callback behavior.
  • Changed-file pre-commit passes (ruff, pycodestyle, pyright, import rules).

This PR was created by an AI agent (OpenHands) on behalf of neubig.

…n callback

Include failure metadata in the workspace completion callback so automations
can attribute outcomes and count them toward auto-disablement:

- On a failing exit, classify the exception into the shared SDK failure
  vocabulary and send it as `failure_kind`.
- Add `BaseWorkspace.register_blocking_factor(kind, reason)` so an agent
  can mark a run that "succeeded but was blocked" (e.g. a configured MCP
  integration was unreachable); the kind and reason are sent in the callback
  as `failure_kind` / `blocking_reason`.

The automation service (`OpenHands/automation`) accepts these fields on its
run completion endpoint and uses them to persist blocking metadata and
auto-disable chronically unhealthy automations.

Co-authored-by: openhands <openhands@all-hands.dev>
@github-actions

github-actions Bot commented Aug 11, 2026

Copy link
Copy Markdown
Contributor

Python API breakage checks — ✅ PASSED

Result:PASSED

Action log

@github-actions

github-actions Bot commented Aug 11, 2026

Copy link
Copy Markdown
Contributor

REST API breakage checks (OpenAPI) — ✅ PASSED

Result:PASSED

Action log

@github-actions

github-actions Bot commented Aug 11, 2026

Copy link
Copy Markdown
Contributor

Coverage

Coverage Report •
FileStmtsMissCoverMissing
openhands-sdk/openhands/sdk/conversation/impl
   local_conversation.py10788792%161, 170, 623–624, 657, 699, 1027, 1051–1052, 1057, 1072, 1074, 1178, 1194, 1242, 1268, 1348, 1352–1358, 1423, 1443–1445, 1494, 1513–1515, 1839–1840, 1855, 2078, 2081–2082, 2107, 2138, 2144, 2225, 2232, 2235, 2238, 2242–2243, 2247–2248, 2251, 2258, 2283, 2287, 2290, 2309, 2361, 2364, 2403, 2410–2411, 2419, 2423–2425, 2432, 2469–2475, 2478, 2481, 2488, 2563, 2568, 2688–2689, 2707–2708, 2741, 2945, 2949, 3019, 3026–3027
   remote_conversation.py7558189%91, 166, 193, 206, 208–211, 237, 255–256, 267–270, 277–278, 283–286, 370, 380–382, 388, 466, 613–616, 618, 644–648, 653–656, 659, 675, 813, 871–872, 876–877, 891, 905–908, 949, 962–963, 987–988, 1028–1029, 1065, 1075, 1079, 1088–1089, 1128, 1278–1279, 1376–1377, 1381, 1386–1390, 1412, 1417, 1467, 1736–1737
openhands-sdk/openhands/sdk/workspace
   base.py73396%23, 265, 276
TOTAL395281116672% 

Remove callback-side exception reclassification and the premature blocked-run
API. Conversation close now registers the FailureKind already attached to the
latest ConversationErrorEvent, and the workspace completion callback transports
that single field.

Co-authored-by: openhands <openhands@all-hands.dev>
@neubig neubig changed the title feat: report automation failure kind and blocking factor in completion callback feat: include classified conversation failures in automation callbacks Aug 11, 2026
Replace mutable workspace failure registration with the existing typed
conversation error. ConversationRunError now carries the authoritative
ConversationErrorEvent emitted during the failed run. The workspace
completion callback serializes that event directly as the callback error,
falling back to a classified ConversationErrorEvent for failures outside a
conversation.

Co-authored-by: openhands <openhands@all-hands.dev>
@neubig neubig changed the title feat: include classified conversation failures in automation callbacks feat: carry ConversationErrorEvent on ConversationRunError for automation callbacks Aug 11, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant