Skip to content

Conversation

@stefandevo
Copy link
Contributor

@stefandevo stefandevo commented Jan 12, 2026

Summary

Fixes an infinite loop that occurs when navigating to the Settings screen while Codex is not connected/authenticated.

Problem

When the user is not connected to Codex, the /api/codex/models endpoint returns 503 Service Unavailable. The frontend's fetchCodexModels() function had no cooldown after failures, causing infinite retries:

OPTIONS /api/codex/models 204
GET /api/codex/models 503
OPTIONS /api/codex/models 204
GET /api/codex/models 503
OPTIONS /api/codex/models 204
GET /api/codex/models 503
... (infinite loop)

Root Cause

  1. Frontend assumes hasApiKey: true means "Codex is ready" - but this only means an API key is installed locally, not that the user is authenticated to the Codex server
  2. No circuit breaker or backoff on 503 errors - when the server returns 503 (unavailable), the frontend keeps retrying without delay
  3. The 5-minute cache check doesn't prevent retries on errors - codexModelsLastFetched is only set on success, so there's no cooldown between failed attempts

Solution

Added a codexModelsLastFailedAt state field to track when the last fetch failure occurred. The fetchCodexModels() function now:

  • Checks if a failure occurred within the last 30 seconds before retrying
  • Records codexModelsLastFailedAt when a fetch fails
  • Clears codexModelsLastFailedAt on successful fetches

This prevents the infinite loop while still allowing periodic retry attempts.

Changes

  • apps/ui/src/store/app-store.ts: Added codexModelsLastFailedAt state and cooldown logic

Test Plan

  • Start the app without Codex connected
  • Navigate to Settings screen
  • Confirm only 1-2 requests to /api/codex/models appear (not infinite)
  • Wait 30+ seconds, navigate away and back to Settings
  • Confirm another request is made (cooldown expired)
  • Connect to Codex and verify models load successfully

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Bug Fixes
    • Improved model-fetch error handling: after a failure, automatic retry is delayed (30s cooldown) and successful results are cached briefly (5min). This prevents rapid repeated attempts, clears errors on recovery, and reduces unnecessary requests for greater UI stability.

✏️ Tip: You can customize this high-level summary in your review settings.

…reen

When Codex is not connected/authenticated, the /api/codex/models endpoint
returns 503. The fetchCodexModels function had no cooldown after failures,
causing infinite retries when navigating to the Settings screen.

Added codexModelsLastFailedAt state to track failed fetch attempts and
skip retries for 30 seconds after a failure. This prevents the infinite
loop while still allowing periodic retry attempts.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@coderabbitai
Copy link

coderabbitai bot commented Jan 12, 2026

📝 Walkthrough

Walkthrough

Added per-file failure tracking and a retry cooldown for Codex models fetches in the app store. A new codexModelsLastFailedAt state field records the last failure timestamp; fetches are skipped for 30 seconds after a failure and the timestamp is cleared on success. (≤50 words)

Changes

Cohort / File(s) Summary
Codex Models Failure Tracking
apps/ui/src/store/app-store.ts
Added `codexModelsLastFailedAt: number

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~12 minutes

Poem

🐇 I nibble on bytes and store with care,
I note when Codex hiccups in the air,
Thirty seconds to breathe and reset,
Then we try again — no need to fret,
Hoppity hops, the models mend with flair.

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the main fix: preventing infinite loop in Codex model fetching by adding failure cooldown logic.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@gemini-code-assist
Copy link
Contributor

Summary of Changes

Hello @stefandevo, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request addresses a critical issue where the application would enter an infinite loop of API calls when attempting to fetch Codex models if the Codex service was unavailable. By introducing a 30-second cooldown period after a failed fetch attempt, the change prevents excessive network requests and improves the application's resilience and user experience during service outages, ensuring that the UI remains responsive.

Highlights

  • Prevent Infinite Loop on Codex Model Fetching: Implemented a 30-second cooldown mechanism for fetchCodexModels() to prevent continuous retries when the Codex API returns a 503 (Service Unavailable) error, specifically when navigating to the Settings screen without Codex connected.
  • Introduce codexModelsLastFailedAt State: Added a new state field codexModelsLastFailedAt to the application store to track the timestamp of the last failed attempt to fetch Codex models, enabling the new cooldown logic.

🧠 New Feature in Public Preview: You can now enable Memory to help Gemini Code Assist learn from your team's feedback. This makes future code reviews more consistent and personalized to your project's style. Click here to enable Memory in your admin console.

Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

@stefandevo stefandevo changed the base branch from main to v0.11.0rc January 12, 2026 23:50
Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request effectively resolves an infinite loop that occurred when fetching Codex models on the settings screen. The approach of adding a codexModelsLastFailedAt state and implementing a failure cooldown is a solid solution to prevent rapid retries on 503 errors. The code changes are clear and well-targeted. I have one suggestion to improve the maintainability of the time-based constants used for caching and cooldowns.

Comment on lines 3124 to 3128
const FAILURE_COOLDOWN = 30000; // 30 seconds
if (
!forceRefresh &&
codexModelsLastFailedAt &&
Date.now() - codexModelsLastFailedAt < FAILURE_COOLDOWN
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

For better readability and maintainability, it's good practice to express time-based constants as calculations (e.g., 30 * 1000) to make the units explicit. Appending _MS to the constant name would also improve clarity.

Additionally, for consistency, consider applying the same treatment to the magic number 300000 used for the success cache duration on line 3134. Ideally, both constants would be defined at a higher scope (e.g., outside this function) to make them easier to find and manage.

Suggested change
const FAILURE_COOLDOWN = 30000; // 30 seconds
if (
!forceRefresh &&
codexModelsLastFailedAt &&
Date.now() - codexModelsLastFailedAt < FAILURE_COOLDOWN
const FAILURE_COOLDOWN_MS = 30 * 1000; // 30 seconds
if (
!forceRefresh &&
codexModelsLastFailedAt &&
Date.now() - codexModelsLastFailedAt < FAILURE_COOLDOWN_MS

- Rename FAILURE_COOLDOWN to FAILURE_COOLDOWN_MS with explicit calculation
- Add SUCCESS_CACHE_MS constant to replace magic number 300000
- Use multiplication (30 * 1000, 5 * 60 * 1000) to make units explicit

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@stefandevo
Copy link
Contributor Author

@coderabbitai can you review this PR

@coderabbitai
Copy link

coderabbitai bot commented Jan 12, 2026

🧩 Analysis chain

@stefandevo, I'll review this PR for you. Let me fetch the changes first.


🏁 Script executed:

#!/bin/bash
# Fetch the PR diff to review the changes
gh pr view 455 --json files,additions,deletions --repo AutoMaker-Org/automaker

# Get the actual diff of the changed file
gh pr diff 455 --repo AutoMaker-Org/automaker

Length of output: 2675


@stefandevo, I'll review this PR now!

Overview

This is a solid fix for the infinite retry loop issue. The implementation correctly addresses the root cause by adding a cooldown mechanism after failed requests. Here are my observations:

✅ Strengths

  1. Clean solution: The addition of codexModelsLastFailedAt with a 30-second cooldown effectively prevents the infinite loop
  2. Good refactoring: Extracting 300000 into SUCCESS_CACHE_MS constant improves readability
  3. Proper recovery logic: Clearing codexModelsLastFailedAt on success ensures the system recovers gracefully
  4. Correct ordering: The failure cooldown check comes before the success cache check, which is the right priority

💡 Suggestions

1. Consider exponential backoff for repeated failures

The current implementation uses a fixed 30-second cooldown. If Codex remains unavailable for an extended period and users keep manually refreshing, you might want to consider exponential backoff:

// Could track failure count and increase cooldown
codexModelsFailureCount: number;
// Then: FAILURE_COOLDOWN_MS * Math.pow(2, Math.min(failureCount, 5))

However, for the current use case, the fixed 30-second cooldown is reasonable and simpler.

2. Add console logging for debugging

Consider adding a debug log when requests are skipped due to cooldown:

if (
  !forceRefresh &&
  codexModelsLastFailedAt &&
  Date.now() - codexModelsLastFailedAt < FAILURE_COOLDOWN_MS
) {
  console.debug('Codex models fetch skipped: in failure cooldown period');
  return;
}

3. Document the forceRefresh bypass behavior

The forceRefresh flag bypasses both cooldowns. Consider adding a JSDoc comment to clarify this behavior:

/**
 * Fetches available Codex models.
 * @param forceRefresh - If true, bypasses both success cache and failure cooldown
 */
fetchCodexModels: async (forceRefresh = false) => {

🤔 Questions

  1. Is 30 seconds the right cooldown? Have you tested this with actual users? It's a reasonable default, but you might want to adjust based on feedback.

  2. What triggers forceRefresh=true? If there's a manual refresh button, this could allow users to bypass the cooldown (which might be desired behavior).

✨ Minor Note

The code already has protection against concurrent fetches via the codexModelsLoading flag, which is good. The race condition handling is adequate for this use case.

Verdict

LGTM! This is a well-thought-out fix that solves the immediate problem without over-engineering. The implementation is clean, maintainable, and addresses the root cause described in the PR objectives.

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (1)
apps/ui/src/store/app-store.ts (1)

3117-3173: Solid fix for the infinite retry loop.

The implementation correctly prevents repeated fetch attempts by:

  1. Recording failure timestamps for cooldown enforcement
  2. Checking failure cooldown before success cache (correct ordering)
  3. Clearing failure state on success to enable immediate recovery

The named constants (FAILURE_COOLDOWN_MS, SUCCESS_CACHE_MS) improve readability over magic numbers.

💡 Optional: Add debug logging when fetch is skipped due to cooldown

This would help with debugging if users report delayed model loading:

     // Skip if recently failed and not forcing refresh
     if (
       !forceRefresh &&
       codexModelsLastFailedAt &&
       Date.now() - codexModelsLastFailedAt < FAILURE_COOLDOWN_MS
     ) {
+      logger.debug('Skipping Codex models fetch - within failure cooldown');
       return;
     }
📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e4458b8 and 18d82b1.

📒 Files selected for processing (1)
  • apps/ui/src/store/app-store.ts
🧰 Additional context used
📓 Path-based instructions (2)
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (CLAUDE.md)

Always import from shared packages (@automaker/*), never from old relative paths

Files:

  • apps/ui/src/store/app-store.ts
**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

Use resolveModelString() from @automaker/model-resolver to convert model aliases (haiku, sonnet, opus) to full model names

Files:

  • apps/ui/src/store/app-store.ts
🔇 Additional comments (2)
apps/ui/src/store/app-store.ts (2)

692-692: LGTM!

The new state field follows the existing naming and type conventions for timestamp tracking (consistent with codexModelsLastFetched).


1290-1290: LGTM!

Correct initialization to null - consistent with other timestamp fields and appropriate for a fresh application state.

@webdevcody webdevcody merged commit b759661 into AutoMaker-Org:v0.11.0rc Jan 13, 2026
3 of 7 checks passed
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.

2 participants