Skip to content

fix: Redis outage rate-limit fallback and provider-valid session history - #56

Merged
getdownandcode merged 3 commits into
mainfrom
fix/ratelimit-and-session-repair
Oct 18, 2026
Merged

getdownandcode merged 3 commits into
mainfrom
fix/ratelimit-and-session-repair

Conversation

@getdownandcode

Copy link
Copy Markdown
Owner

Fixes #55

Summary

Resolves the two medium-severity bugs reported in #55.

1. Rate limiter degrades gracefully on Redis outage (agentkit/api/ratelimit.py)

_check_window only caught ResponseError, so a genuine outage (ConnectionError / TimeoutError, siblings of ResponseError) escaped the rate_limit dependency and surfaced as HTTP 500 on /runs and /tools. check() now catches RedisError, logs a warning, and falls back to the in-memory sliding window (extracted into _check_window_in_memory, shared with the no-Redis path). When Redis is reachable, behavior is unchanged.

2. Aborted runs persist provider-valid session history (agentkit/core/agent.py)

A run-level timeout mid-tool, a duplicate-loop abort mid-batch, or an unexpected error could end the run after the assistant tool-call turn was appended but before all tool results were. The saved history then contained dangling assistant.tool_calls, which OpenAI rejects with a 400 and Gemini requires a function_response for — poisoning the session until TTL expiry. _repair_trailing_tool_calls now appends synthetic error tool results for unanswered call ids (preserving partial results) before save_messages on every terminal path, including raise_on_failure=True.

Tests

  • tests/unit/test_api_ratelimit.py: unreachable-Redis stub — limiter enforces the limit via memory; API-level 200/200/429 (previously 500).
  • tests/unit/test_agent_guards.py: timeout mid-tool persists a valid history; follow-up run in the same session succeeds.
  • tests/unit/test_agent_loop_detector.py: duplicate abort (whole turn + mid-batch) answers every call id.
  • All new tests were confirmed to fail against the pre-fix code.

Verification

  • pytest — full hermetic suite green; pytest --cov=agentkit --cov-fail-under=85 passes (92% total).
  • ruff check ., ruff format --check ., mypy agentkit tests — clean.

- Catch RedisError (not just ResponseError) around the sliding-window script so
  connection failures degrade to the in-memory window instead of surfacing as 500
- Extract the in-memory window into _check_window_in_memory shared by both paths
- Add unit and API-level regression tests for an unreachable Redis client
- Repair the trailing assistant tool-call turn before saving session messages so
  timeouts, duplicate-loop aborts, and unexpected errors cannot leave dangling
  tool_calls that OpenAI and Gemini reject on the next run
- Append synthetic error tool results for unanswered call ids, preserving any
  partial results already recorded
- Add regression tests covering timeout, duplicate abort, and mid-batch abort
@getdownandcode
getdownandcode merged commit 552aac8 into main Oct 18, 2026
4 checks passed
@getdownandcode
getdownandcode deleted the fix/ratelimit-and-session-repair branch October 18, 2026 16:44
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Rate limiter 500s on Redis outage and aborted runs persist dangling tool calls

1 participant