Skip to content

[Bug]: /goal can spam repeated completion messages when goal_judge errors fail-open to continue #27585

@0oAstro

Description

@0oAstro

Summary

A persistent /goal loop can spam a gateway channel with repeated near-identical "Goal is complete / I'm stopping" messages when the assistant has reached a terminal answer but the goal judge fails with an API/transport error. The current fail-open behavior treats judge errors as continue, so the goal loop immediately queues another continuation prompt; the agent then repeats the same terminal completion message, and this can happen repeatedly in Discord/Telegram/etc.

This is different from false-positive goal completion (#18421) and explicit-incomplete completion (#26986): here the assistant is explicitly trying to stop, but the judge cannot evaluate and the loop keeps driving more turns.

Observed behavior

In a gateway session, the same completion message was sent repeatedly every few seconds, e.g. variants of:

Goal is complete.

The requested artifact is already done and saved at:

/path/to/generated_artifact.md

I'm stopping here because the requested goal is complete.

The persisted goal state showed that the goal had not been marked done by the judge:

{
  "status": "cleared",
  "turns_used": 16,
  "max_turns": 20,
  "last_verdict": "continue",
  "last_reason": "judge error: BadRequestError",
  "consecutive_parse_failures": 0
}

The assistant messages in the same session contained many repeated terminal responses pointing to the same generated artifact path.

Expected behavior

If the goal judge errors while the assistant's latest response is explicitly terminal (e.g. "Goal is complete", "I'm stopping here", "requested goal is complete"), Hermes should not blindly continue the /goal loop and spam the messaging platform.

At minimum one of these should happen:

  1. Pause the goal with a user-visible notice such as "goal judge errored; assistant appears to have stopped/completed, pausing to avoid spam".
  2. Treat terminal completion phrasing as a safe stop condition only when the judge is unavailable.
  3. Detect repeated near-identical final responses and auto-pause after 1-2 repeats.
  4. Suppress repeated duplicate gateway deliveries from goal continuations.

Actual behavior

judge_goal() catches exceptions and returns:

return "continue", f"judge error: {type(exc).__name__}", False

Then GoalManager.evaluate_after_turn() keeps the state active and returns should_continue=True as long as the turn budget has not been exhausted. The continuation prompt asks the agent to continue or stop if complete, so the agent repeatedly replies with a terminal completion message, but the judge keeps failing open to continue.

Likely root cause area

  • hermes_cli/goals.py::judge_goal() exception path
  • hermes_cli/goals.py::GoalManager.evaluate_after_turn() handling for last_reason like judge error: BadRequestError
  • Gateway post-turn goal continuation paths that queue the next turn when decision["should_continue"] is true

Relevant code shape:

# hermes_cli/goals.py
except Exception as exc:
    logger.info("goal judge: API call failed (%s) — falling through to continue", exc)
    return "continue", f"judge error: {type(exc).__name__}", False

Suggested fix

Add a spam-safe fallback before returning should_continue=True on judge API errors:

  • Detect explicit terminal phrases in last_response, e.g.:
    • goal is complete
    • requested goal is complete
    • i'm stopping
    • i am stopping here
    • stopping because .* complete
  • If the judge errored and the response is terminal, set status to paused (or done, if maintainers prefer) with paused_reason="goal judge error after terminal assistant response".
  • Add regression coverage for:
    1. judge raises BadRequestError/generic exception;
    2. assistant response says "Goal is complete... I'm stopping...";
    3. evaluate_after_turn() returns should_continue=False and does not queue another continuation;
    4. non-terminal responses still fail open to continue as today.

A second defense-in-depth guard could detect repeated near-identical final responses in a goal loop and pause, even if the text does not match the explicit phrase list.

Impact

  • Gateway users can receive repeated spam messages in Discord/Telegram/etc.
  • The loop consumes model calls until max goal turns are exhausted or the user manually interrupts/clears the goal.
  • The behavior is confusing because the assistant says it is stopping, but the outer goal controller keeps restarting it.

Related issues

This issue is the inverse operational failure: assistant response is terminal, but judge errors fail-open to continue and causes repeated terminal-message spam.

Metadata

Metadata

Assignees

No one assigned

    Labels

    P2Medium — degraded but workaround existscomp/agentCore agent loop, run_agent.py, prompt buildertype/bugSomething isn't working

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions