Skip to content

[Bug]: cron path still emits HTTP 400 reasoning_content error on deepseek-v4-flash after #15213 closure #15741

@gth-ai

Description

@gth-ai

Bug Description

A recurring cron job continues to fail with the DeepSeek reasoning_content 400 error after the closure of #15213 (which had been filed for the same root cause in the cron path). Three consecutive runs across 2026-04-24 and 2026-04-25 produced the same Non-retryable client error on Hermes v0.11.0.

This issue is filed as a follow-up to:

The closure of #15213 may have addressed the main-loop case but did not cover the cron auxiliary path, since failures continued to occur after that closure.

Steps to Reproduce

  1. Hermes Agent v0.11.0 (2026.4.23), Python 3.11.14, OpenAI SDK 2.32.0.
  2. Create a recurring cron job whose model field is set to deepseek-v4-flash and provider to deepseek-v4. The prompt must require at least one tool call before producing the final response (any multi-step prompt that reads from a tool and then summarizes works).
  3. Let the cron fire on its scheduled trigger.
  4. Inspect ~/.hermes/logs/agent.log (or errors.log) and the cron output file under ~/.hermes/cron/output/<job_id>/<date>.md.

Expected Behavior

The agent completes the multi-turn tool-call workflow and the final response is delivered via deliver: origin.

Actual Behavior

The agent makes one or more tool calls successfully, then on a follow-up turn the API request returns HTTP 400. Reproduced verbatim from errors.log (job ID and timestamps anonymized only with respect to the run number — error string is unmodified):

ERROR [cron_<job_id>_20260424_133042] root: Non-retryable client error: Error code: 400 - {'error': {'message': 'The `reasoning_content` in the thinking mode must be passed back to the API.', 'type': 'invalid_request_error', 'param': None, 'code': 'invalid_request_error'}}
ERROR [cron_<job_id>_20260424_142831] root: Non-retryable client error: Error code: 400 - {'error': {'message': 'The `reasoning_content` in the thinking mode must be passed back to the API.', 'type': 'invalid_request_error', 'param': None, 'code': 'invalid_request_error'}}
ERROR [cron_<job_id>_20260425_133011] root: Non-retryable client error: Error code: 400 - {'error': {'message': 'The `reasoning_content` in the thinking mode must be passed back to the API.', 'type': 'invalid_request_error', 'param': None, 'code': 'invalid_request_error'}}

The cron output file then contains:

## Response

(No response generated)

…and the job is marked last_status: error with the synthesized message Agent completed but produced empty response (model error, timeout, or misconfiguration). No retry. No delivery.

Why this is not covered by #15213

#15213 reports _copy_reasoning_content_for_api works in the main loop but breaks in the cron auxiliary path "after several auxiliary calls (title_generation, vision_analyze, auxiliary auto-detect)". The case here has the same surface symptom but a simpler trigger:

This suggests the reasoning_content passthrough is missing in the cron path's primary tool-result → next-turn handoff, not just the auxiliary calls listed in #15213.

Minimal reproduction

A minimal cron job definition that triggers the bug (extracted from ~/.hermes/cron/jobs.json):

{
  "schedule": {"kind": "cron", "expr": "<any cron expression>"},
  "model": "deepseek-v4-flash",
  "provider": "deepseek-v4",
  "deliver": "origin",
  "enabled": true,
  "prompt": "<any prompt that requires one tool call before the final response>"
}

A minimal repro prompt:

Read the README at https://github.com/<any-public-repo> and return three bullet points summarizing it. After reading, respond with the bullets.

The model performs the read, then the next request to the DeepSeek API returns 400.

Workaround

Switching the job to model: gpt-5.3-codex / provider: openai-codex (or any non-thinking provider) restores normal operation.

Environment

Suggested fix

Audit every code path that builds the next API request from prior turns — not just run_agent.py::_copy_reasoning_content_for_api — and ensure reasoning_content from the previous assistant message is preserved when the message is sent back to a thinking-mode model. The cron path appears to be one such missing site; auxiliary calls (per #15213) are another.

Defense-in-depth option: when the provider is deepseek-v4 and the next request targets a model name matching *-flash/*-pro/*-thinking, validate that the last assistant turn carries reasoning_content before sending; if not, refuse the request locally with a clearer error than the upstream HTTP 400.

Metadata

Metadata

Assignees

No one assigned

    Labels

    P2Medium — degraded but workaround existscomp/cronCron scheduler and job managementprovider/deepseekDeepSeek APItype/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