Placeholder text leaking to users and cron targets
NousResearch/hermes-agent · cluster 2234 · agent
Data as of
Nothing open on GitHub. Fix merged in #2442. Check it covers your case.
The short answer
- Known?
- Yes. 13 related items grouped here, 4 issues already closed.
- Fix event
- A fix was merged to main in PR #2442. Check it covers your case. No release data is tracked.
- Tracker
- The maintainers' tracker marks this
partially resolved, deciding PRs #2442, #11021. That is separate from the fix event. - Still open
- Nothing open on GitHub.
Multiple bug reports and fix attempts address a problem where internal fallback strings like '(No response generated)' or '(empty)' are delivered to end users and cron job targets instead of being suppressed. The cluster covers both the detection of empty/think-only LLM responses and the separation of logging placeholders from actual delivery responses.
Open issues0
Open PRs0
Fixes on record
-
#2442 fix(cron): silent jobs return empty response for delivery skip
files:
cron/scheduler.py,tests/cron/test_scheduler.pyShow the diff
-
cron/scheduler.py@@ -412,9 +412,10 @@ def run_job(job: dict) -> tuple[bool, str, str, Optional[str]]: result = agent.run_conversation(prompt) - final_response = result.get("final_response", "") - if not final_response: - final_response = "(No response generated)" + final_response = result.get("final_response", "") or "" + # Use a separate variable for log display; keep final_response clean + # for delivery logic (empty response = no delivery). + logged_response = final_response if final_response else "(No response generated)" output = f"""# Cron Job: {job_name} @@ -428,7 +429,7 @@ def run_job(job: dict) -> tuple[bool, str, str, Optional[str]]: ## Response -{final_response} +{logged_response} """ logger.info("Job '%s' completed successfully", job_name) -
tests/cron/test_scheduler.py@@ -234,6 +234,47 @@ def test_run_job_passes_session_db_and_cron_platform(self, tmp_path): assert kwargs["session_id"].startswith("cron_test-job_") fake_db.close.assert_called_once() + def test_run_job_empty_response_returns_empty_not_placeholder(self, tmp_path): + """Empty final_response should stay empty for delivery logic (issue #2234). + + The placeholder '(No response generated)' should only appear in the + output log, not in the returned final_response that's used for delivery. + """ + job = { + "id": "silent-job", + "name": "silent test", + "prompt": "do work via tools only", + } + fake_db = MagicMock() + + with patch("cron.scheduler._hermes_home", tmp_path), \ + patch("cron.scheduler._resolve_origin", return_value=None), \ + patch("dotenv.load_dotenv"), \ + patch("hermes_state.SessionDB", return_value=fake_db), \ + patch( + "hermes_cli.runtime_provider.resolve_runtime_provider", + return_value={ + "api_key": "test-key", + "base_url": "https://example.invalid/v1", + "provider": "openrouter", + "api_mode": "chat_completions", + }, + ), \ + patch("run_agent.AIAgent") as mock_agent_cls: + mock_agent = MagicMock() + # Agent did work via tools but returned no text + mock_agent.run_conversation.return_value = {"final_response": ""} + mock_agent_cls.return_value = mock_agent + + success, output, final_response, error = run_job(job) + + assert success is True + assert error is None + # final_response should be empty for delivery logic to skip + assert final_response == "" + # But the output log should show the placeholder + assert "(No response generated)" in output + def test_run_job_sets_auto_delivery_env_from_dotenv_home_channel(self, tmp_path, monkeypatch): job = { "id": "test-job",
-
-
#11021 fix: empty response nudge crash + placeholder leak to cron targets
files:
cron/scheduler.py,gateway/run.py,run_agent.py,scripts/release.pyShow the diff
-
cron/scheduler.py@@ -837,6 +837,9 @@ def run_job(job: dict) -> tuple[bool, str, str, Optional[str]]: ) final_response = result.get("final_response", "") or "" + # Strip leaked placeholder text that upstream may inject on empty completions. + if final_response.strip() == "(No response generated)": + final_response = "" # Use a separate variable for log display; keep final_response clean # for delivery logic (empty response = no delivery). logged_response = final_response if final_response else "(No response generated)" -
gateway/run.py@@ -8886,7 +8886,7 @@ def _approval_notify_sync(approval_data: dict) -> None: _resolved_model = getattr(_agent, "model", None) if _agent else None if not final_response: - error_msg = f"⚠️ {result['error']}" if result.get("error") else "(No response generated)" + error_msg = f"⚠️ {result['error']}" if result.get("error") else "" return { "final_response": error_msg, "messages": result.get("messages", []), -
run_agent.py@@ -10945,8 +10945,9 @@ def _stop_spinner(): # tool(result) → assistant("(empty)") → user(nudge) # Without this, we'd have tool → user which most # APIs reject as an invalid sequence. - assistant_msg["content"] = "(empty)" - messages.append(assistant_msg) + _nudge_msg = self._build_assistant_message(assistant_message, finish_reason) + _nudge_msg["content"] = "(empty)" + messages.append(_nudge_msg) messages.append({ "role": "user", "content": ( -
scripts/release.py@@ -179,6 +179,7 @@ "[redacted:email]": "limars874", "[redacted:email]": "lesterli", "[redacted:email]": "MagicRay1217", + "[redacted:email]": "GenKoKo", "[redacted:email]": "spniyant", "[redacted:email]": "olafthiele", "[redacted:email]": "sprmn24",
-
7 PRs closed without merging
From the close comments: done elsewhere 2 · merged via another change 2 · closed without comment 1 · other 2
-
#2238 fix: separate cron log placeholder from delivery response · other →
#1741 -
#2241 fix(cron): separate log placeholder from delivery response for silent cron jobs · done elsewhere →
#2442 -
#2255 fix(cron): treat empty agent response as silent instead of delivering fallback text · other →
#2442 -
#2280 fix(cron): silent jobs return empty response for delivery skip · merged via another change →
#2442 - #2407 fix: separate cron log placeholder from delivery response (salvage #2238) · closed without comment
Closed issues · 4
- Closed#2234bug(cron): silent cron jobs deliver "(No response generated)" to Discord
- Closed#6559[Bug] Ollama concurrency limit causes literal '(empty)' responses on Telegram
- Closed#9270Empty LLM responses get replaced with literal "(No response generated)" and delivered to users, including cron targets
- Closed#11539bug: literal (empty) string sent to users when model returns no content
How this cluster was grouped
-
Link evidence (
partly structural links): some members are connected by structural evidence (shared changed files, or a closing/superseded reference); the rest are joined by an automated relevance judgement over their text. - Members outside that structural core may belong to a different underlying problem.
-
Pairwise link density
0.13: the share of item pairs that carry a link, counting a structural link in full and a relevance-only link at 0.36.1.00means every pair is linked.
Pick up a Lab task
Check whether a fix covers the open issues here, with your own agent. Bring back evidence for maintainers.
Open the Lab