Skip to content

fix: reject inline tasks during agent handoff - #7268

Open
chenghao-mou wants to merge 4 commits into
mainfrom
chenghao/fix/AGT-3502-inline-task-handoff-deadlock
Open

chenghao-mou wants to merge 4 commits into
mainfrom
chenghao/fix/AGT-3502-inline-task-handoff-deadlock

Conversation

@chenghao-mou

@chenghao-mou chenghao-mou commented Sep 14, 2026

Copy link
Copy Markdown
Member

An outgoing tool can deadlock handoff by awaiting an AgentTask. Reject that task with a transition ToolError from handoff request through drain.

Reproduces @dtran26's report in agents-js#2483. The Python fix was developed independently; the similar JS fix was discovered afterward.

147 focused tests and core type checks pass. Full plugin type checks are blocked by environment errors.

Addresses AGT-3502.

Initial prompt and agent context

Model: GPT-6

can you investigate /tmp/agents-python-2483-investigation.md

Essential follow-up instructions:

how about we raise a tool error if the parent acitivity is draining and let the agent to call it later?

Yeah, it should be obvious to the agent that we are in transition to a new agent, and a tool call is not permitted until the transition is finished

I thought we have handoff first in the initial failure case?

Reject inline tasks from an outgoing activity once handoff starts. This breaks
the circular wait between activity shutdown and a tool awaiting the transition
lock, while preserving normal inline-task pause and resume.

Co-authored-by: Dan Tran <danganhtran@me.com>
@chenghao-mou chenghao-mou added the review_effort:high Needs broad context and substantial validation label Sep 14, 2026
@chenghao-mou
chenghao-mou marked this pull request as ready for review September 14, 2026 14:29
@chenghao-mou
chenghao-mou requested a review from a team as a code owner September 14, 2026 14:30
devin-ai-integration[bot]

This comment was marked as resolved.

@devin-ai-integration devin-ai-integration Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Devin Review found 1 new potential issue.

Devin Review

@devin-ai-integration devin-ai-integration Bot Sep 14, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔴 Public drain deadlocks inline tasks

When AgentSession.drain() overlaps a tool awaiting an AgentTask, drain admits that inline task. The task waits for the activity lock while drain waits for its scheduling task, so both hang.

Learn more

AgentSession.drain() calls this method directly without going through _update_activity, so no other path sets _new_turns_blocked. An admitted inline AgentTask calls _update_activity(..., previous_activity="pause"), which eventually needs this activity's lock. The ongoing drain owns that lock and waits for _scheduling_atask, which contains the tool awaiting the inline task. Neither operation can finish.

Example: A non-cancellable tool pauses before await InlineTask(). Another coroutine starts session.drain(), then releases the tool. The inline task passes _inline_task_slot, waits to pause the locked activity, and the drain waits for that same tool to finish.

Recommended fix: Preserve admission blocking for a successful public drain, but roll it back if the drain is cancelled before scheduling pauses. Keep handoff blocking synchronous in _update_activity and avoid clearing a flag established by another concurrent transition.

Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The agent_activity.py:2067 permits this because the excluded tool no longer counts as pending work.

The handoff case has an additional dependency: after drain, activity.aclose() waits for the tool executor while the handoff still holds the session’s _activity_lock. That prevents the inline task’s _update_activity() from proceeding. Public drain does neither.

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

Labels

review_effort:high Needs broad context and substantial validation

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant