1jehuang / 1jehuang/jcode

[bug] Auto-retry backoff and max attempts are hardcoded, causing premature agent stops on shared gateways

Open
#677 9 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

autonomous: likely enhancement priority: medium triage: needs-decision
Dominant language
Rust
Stars
19.9k
Forks
2.3k
Avg merge
2d 7h
Merged PRs (30d)
30

Description

What happens

When using jcode with a shared LLM gateway (for example, a proxy that many people use at the same time), the agent stops after only 3 retries. The backoff is 2s, 4s, 6s. This is too fast for a shared service. The gateway often returns 429 Too Many Requests without a retry-after header. The agent runs out of retries before the gateway has time to free capacity, so the turn dies.

Root cause: hardcoded retry constants

The retry parameters are hardcoded in crates/jcode-tui/src/tui/app.rs:

const AUTO_RETRY_BASE_DELAY_SECS: u64 = 2;
const AUTO_RETRY_MAX_ATTEMPTS: u8 = 3;

This is a bug. These values are fixed in the binary and you cannot change them. Other retry settings like stream_idle_timeout_secs and auto_poke are configurable in config.toml or with env vars, but the retry budget is not. There is no way for a user to adjust the backoff or the number of retries for their provider.

When the gateway returns 429 without a parseable retry-after or reset time, jcode uses these hardcoded values. Three attempts at 2s/4s/6s is not enough for a shared service under load. The agent gives up on a temporary error that would recover if it waited longer.

Impact

Any user on a shared LLM gateway will see the agent stop in the middle of a task when the gateway is briefly busy. Since auto_poke keeps the agent working on its own, a premature retry failure kills the autonomous loop silently. The user does not notice until they check the session.

What this is NOT

This is not about auto_poke itself. Auto-poke is already configurable with [features] auto_poke = true/false. The bug is that the retry budget behind auto-poke is hardcoded and cannot be tuned.

The fix

Branch: fix/auto-retry-config on my fork.

Makes both values configurable. You can set a global default in [provider] and optionally override it per provider in [providers.<name>]. Env vars also work.

# Global defaults
[provider]
auto_retry_base_delay_secs = 2
auto_retry_max_attempts = 3

# Per-provider override (used when that provider is active)
[providers.my-shared-gateway]
auto_retry_base_delay_secs = 10
auto_retry_max_attempts = 10
# Env var overrides (highest priority)
export JCODE_AUTO_RETRY_BASE_DELAY_SECS=10
export JCODE_AUTO_RETRY_MAX_ATTEMPTS=10

Priority: env var > per-provider > global default

The backoff is linear: base_delay * attempt_number. The per-provider value is checked at retry time from the active session provider key. If the provider does not set it, the global value is used.

If the gateway provides a retry-after time in the error message, jcode uses that instead of the linear backoff.

Documentation

The fix also adds a new section to the README (Auto-retry backoff) explaining the configuration, environment variables, and precedence. The default config template in crates/jcode-base/src/config/default_file.rs also documents both keys with comments.

Files changed (7 files, +135/-6)
  • crates/jcode-config-types/src/lib.rs — new fields on ProviderConfig (global) and NamedProviderConfig (per-provider override)
  • crates/jcode-base/src/config/env_overrides.rs — env var parsing
  • crates/jcode-base/src/config/default_file.rs — documentation in default config template
  • crates/jcode-tui/src/tui/app.rs — runtime fields on App (replaces hardcoded consts)
  • crates/jcode-tui/src/tui/app/tui_lifecycle.rseffective_auto_retry_*() methods that check the active provider override at retry time, falling back to the global value
  • crates/jcode-tui/src/tui/app/tests/remote_events_reload_04.rs — update test references
  • README.md — new Auto-retry backoff section with configuration, env vars, and precedence docs

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with the hardcoded retry constants in crates/jcode-tui/src/tui/app.rs, then trace configuration through crates/jcode-config-types/src/lib.rs and crates/jcode-base/src/config/env_overrides.rs. Review the lifecycle methods and remote-events test named in the issue, along with the default template and README. Done means global, per-provider, and environment settings work with the stated precedence and documentation matches.

Written by the indexing model from the issue text.

Assessment

Tech stack
rust
Domain
cli, tooling
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Active
Clarity
Clearly specified
Newbie friendliness
25/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.