Goal future-time waits spin for 100+ auto-continuation turns instead of handing off to heartbeat automation
What version of the Codex App are you using (From “About Codex” dialog)?
26.519.81530 (3178)
What subscription do you have?
ChatGPT Pro
What platform is your computer?
macOS 15.7.3 (24G419), Darwin 24.6.0 arm64 arm
What issue are you seeing?
Goal mode can enter a token-burning auto-continuation loop when the remaining work is only to wait until a future wall-clock time.
In my session, the Goal understood that it should wait until 2026-06-18 06:00 local time before checking status again. However, instead of creating a heartbeat/automation and going idle, Codex kept the Goal Active and repeatedly started immediate automatic continuation turns.
This continued for 100+ automatic Goal continuation turns until I explicitly stopped the Goal and manually enabled automation/heartbeat. After switching to automation, the issue stopped.
This looks like a Goal/Automation handoff bug: Goal mode recognizes that it should wait until a future time, but it does not hand the task off to automation/heartbeat. Instead, it keeps the Goal active and repeatedly starts immediate continuation turns.
Session log excerpt from the repeated Goal continuation turns:
Continue waiting until 6:00. I will not check the cluster now.
Continue waiting until 6:00. I will not check the cluster or refresh status now.
Continue waiting until 6:00. I will check and report after the time arrives.
Wednesday 11:09 PM
Continue waiting until 6:00. I am not checking the cluster right now.
Continue waiting until 6:00. I am not checking status now.
Continue waiting until 6:00. I am not checking the cluster or outputting status now.
Continue waiting until 6:00. I am not checking the cluster or refreshing logs now.
Worked for 15s
Continue waiting until 6:00. I am not checking the cluster or refreshing logs now.
Continue waiting until 2026-06-18 06:00 local time. I am not checking the cluster or refreshing logs now.
The text excerpt above is only a small part of the session log. The actual session repeated this pattern for 100+ automatic Goal continuation turns.
What steps can reproduce the bug?
- Enable Goal mode.
- Start a long-running task.
- Ask Codex to check back at a future time after the task finishes. For example:
``text``
Finish the current task. If it is not yet 2026-06-18 06:00 local time, do not keep checking status or refreshing logs. Check and report after that time.
- Let the actionable work finish before the requested future time.
- Observe Goal auto-continuations.
Actual behavior:
- Codex keeps starting immediate Goal continuation turns.
- The turns repeatedly restate that Codex will wait until the future time.
- No meaningful work can be performed during those turns.
- No heartbeat/automation is created automatically.
- The loop continues until the Goal is manually stopped, a usage/token limit is reached, or the user manually switches to automation.
What is the expected behavior?
When the only remaining work is to wait until a future time or check back later, Codex should:
- Recognize this as a deferred/scheduled state, not immediate work.
- Create a thread heartbeat/automation for the requested absolute time.
- Pause/defer the active Goal so no immediate continuation turns are started.
- Resume/check/report when the heartbeat fires.
The expected behavior is not to keep the Goal active and repeatedly spend continuation turns saying that it is waiting.
Additional information
This is more than noisy output. It can create an unbounded autonomous continuation loop.
Impact:
- Continuous token consumption while no meaningful work can be performed.
- Unnecessary model inference and system resource usage.
- Possible quota/cost waste for the user.
- Repeated low-value turns until the future time, usage limit, token budget, or manual interruption.
- Bad behavior for long-running goals where the user explicitly asks Codex to check back later.
Suspected root cause from the public code:
codex-rs/ext/goal/src/runtime.rsapply_external_goal_set()callscontinue_if_idle()whenever the goal status isActive.continue_if_idle()reads the goal, and if it is stillActive, injects the continuation steering item and callstry_start_turn_if_idle(...).- There does not appear to be a “defer until future wall-clock time” state.
codex-rs/ext/goal/templates/goals/continuation.md- The continuation prompt tells the model to keep working toward the active goal and leave it active if it cannot be completed now.
- It does not mention future-time waits, heartbeat, automation, scheduling, or tool discovery for deferred work.
codex-rs/ext/goal/src/tool.rsupdate_goalonly permitscompleteorblocked.- The model cannot mark a goal as
paused/deferredafter scheduling a future check-back.
Possible fix direction:
- Update Goal continuation guidance so future-time waits/check-backs use heartbeat/automation instead of immediate continuation.
- Provide a safe way for the model/runtime to defer the Goal after scheduling the heartbeat, for example:
- allow
update_goal(status: "paused")only after a heartbeat is successfully scheduled, or - add a dedicated
defer_goal_until(...)/ runtime-level deferred state.
- Ensure the heartbeat resumes the Goal or starts the appropriate follow-up turn at the requested time.
- Add regression coverage asserting that a future-time Goal does not start repeated immediate continuation turns.
I understand that external code contributions to Codex are by invitation only. If the Codex team agrees this is a high-impact bug and the proposed direction is aligned, I would be happy to submit a narrow PR.
Would the Codex team be open to inviting a narrow external PR for this issue?
5 Comments
Potential duplicates detected. Please review them and close your issue if it is a duplicate.
Powered by Codex Action
Same here, seems like if it gets into the "goal" mindset, then it's hard to push it back to thread automations/heartbeat.
I prepared a Goal-side implementation for this handoff and published the reviewable diff here:
https://github.com/vincenthcui/codex/pull/1
The design keeps the Goal
activebut replaces its next-turn trigger:The binding is persisted and guarded by
goalVersion + automationId + generation, so an old automation run cannot affect a replacement Goal or a newer schedule generation.This is intentionally the Goal-side primitive, not a claim that the shipped Desktop flow is already fixed. The current Desktop
automation_updatehandler still needs to install the timer trigger after durable schedule creation and restore it for one-shot/cancel flows.@titanwings, could you review whether the acceptance behavior matches the original 100+ continuation-turn reproduction? @jif-oai, @etraut-openai, and @eternal-openai, maintainer feedback on the lifecycle/API boundary would be very helpful.
I attempted to open the PR directly against
openai/codex, but GitHub rejectedCreatePullRequestfor this account. If this direction is acceptable, guidance or an invitation for the upstream landing path would be appreciated.Follow-up: I added the Desktop integration and protocol migration plan here:
https://github.com/vincenthcui/codex/pull/1#issuecomment-5407480051
It covers the exact handler sequencing for create/update/one-shot/recurring/cancel, two-phase failure recovery,
experimentalApicapability gating, mixed Desktop/app-server versions, existing heartbeat backfill, and safe SQLite rollback.The implementation is now rebased onto current
openai/codex@37f4bb94, verified, and squash-merged into the fork'smain:The compare API reports exactly one commit ahead and zero commits behind.
I attempted to create the upstream PR through both GitHub GraphQL (
gh pr create) and the REST pull-request endpoint. GraphQL reports thatvincenthcuilacksCreatePullRequestpermission; REST returns the repository's permission-hiding 404. This matches the invite-only external contribution policy mentioned in this issue.@jif-oai, @etraut-openai, or @eternal-openai: could one of you invite
vincenthcuito submit this upstream, or open/cherry-pick the compare commit for review? The proposed upstream PR body is ready and the latest verification is:codex-state: 187 passed;codex-goal-extension: 24 passed;codex-app-server-protocol: 295 passed, 1 skipped;