Skip to content

fix(approvals): skip needs_approval_checker when status already resolved#3229

Merged
seratch merged 1 commit intoopenai:mainfrom
adityasingh2400:fix/approvals-audit
May 8, 2026
Merged

fix(approvals): skip needs_approval_checker when status already resolved#3229
seratch merged 1 commit intoopenai:mainfrom
adityasingh2400:fix/approvals-audit

Conversation

@adityasingh2400
Copy link
Copy Markdown
Contributor

Summary

In _select_function_tool_runs_for_resume, the user-supplied needs_approval_checker was awaited unconditionally for every run on the resume path, including runs whose approval status was already resolved (True/False).

The checker's result is unused on those branches, so the eager await was wasted work — but more importantly, if a user-supplied checker raises (which is plausible for checkers that consult external state), the exception propagates out before the loop can reach record_rejection, silently dropping the rejection record for an explicitly-rejected call.

The corresponding helper _collect_runs_by_approval already orders these checks correctly (status checks first, then needs_approval_checker). This change brings _select_function_tool_runs_for_resume in line with that pattern: the checker is only awaited when approval_status is None.

Changes

  • src/agents/run_internal/tool_planning.py: defer await needs_approval_checker(run) until after the approval_status is True/False branches. Drop the unreachable trailing selected.append(run) since approval_status is bool | None and all three cases are covered.
  • tests/test_hitl_error_scenarios.py: add test_resume_skips_needs_approval_checker_when_status_resolved — a checker that raises AssertionError is not invoked for either pre-approved or pre-rejected calls, and the pre-rejected call still hits record_rejection.

Test plan

  • pytest tests/test_hitl_error_scenarios.py tests/test_run_internal_approvals.py (58 passed)
  • pytest tests/ -k "approval or hitl or resume" (190 passed, 0 failed)
  • pytest tests/test_run.py tests/test_run_step_processing.py tests/test_run_step_execution.py tests/test_run_impl_resume_paths.py tests/test_run_context_approvals.py tests/test_run_state.py tests/test_run_internal_approvals.py tests/test_run_internal_items.py (343 passed)

In `_select_function_tool_runs_for_resume`, the user-supplied
`needs_approval_checker` was awaited unconditionally for every run,
even when the approval status was already True or False. The result
was discarded for resolved decisions, so the call was wasted work —
but more importantly, if the checker raised, the rejection branch
(`record_rejection`) was never reached and the rejection was lost.

Reorder so the checker only runs when the approval state is None,
matching the pattern used in `_collect_runs_by_approval`.
@seratch
Copy link
Copy Markdown
Member

seratch commented May 8, 2026

@codex review

@chatgpt-codex-connector
Copy link
Copy Markdown

Codex Review: Didn't find any major issues. 🚀

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@seratch seratch added this to the 0.17.x milestone May 8, 2026
@seratch seratch merged commit 7670257 into openai:main May 8, 2026
10 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working feature:core

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants