Skip to content

fix: stop silently swallowing exceptions in forward_incremental#802

Open
dashitongzhi wants to merge 3 commits into
nottelabs:mainfrom
dashitongzhi:fix/incremental-action-listing-exception-swallowing
Open

fix: stop silently swallowing exceptions in forward_incremental#802
dashitongzhi wants to merge 3 commits into
nottelabs:mainfrom
dashitongzhi:fix/incremental-action-listing-exception-swallowing

Conversation

@dashitongzhi

@dashitongzhi dashitongzhi commented May 7, 2026

Copy link
Copy Markdown

Summary

The retry loop in forward_incremental was catching all exceptions with a bare except Exception: pass, silently discarding all error information. This made it impossible to diagnose why incremental action listing was failing.

Changes

In RetryPipeWrapper.forward_incremental():

  • Collect error messages in an errors list (matching the pattern in forward())
  • Log each failure when verbose mode is on, showing the first 200 chars of the error message
  • Trace failures via self.tracer.trace() so they appear in telemetry/debug output
  • Improved log message when max retries are exhausted: now includes retry count and all error messages

Before

for _ in range(self.max_tries):
    try:
        return await self.pipe.forward_incremental(snapshot, previous_action_list)
    except Exception:
        pass  # 🔇 all errors silently swallowed

After

errors: list[str] = []
last_error: Exception | None = None
for _ in range(self.max_tries):
    try:
        return await self.pipe.forward_incremental(snapshot, previous_action_list)
    except Exception as e:
        last_error = e
        errors.append(str(e))
        if self.verbose:
            logger.debug(f"forward_incremental: failed ... error msg: {str(e)[:200]}...")

This matches the error handling pattern already used in the sibling forward() method.

Summary by CodeRabbit

  • Bug Fixes & Improvements
    • Improved retry behavior for incremental processing: the system now records per-attempt error messages, tracks the last exception, emits a failure event with retry details, and provides clearer debug logging when verbose. If retries exhaust, it returns the prior action set while indicating the fallback in logs.

Note

Fixes silent exception swallowing in forward_incremental by collecting errors, logging them in verbose mode, and reporting failures to the tracer — matching the pattern already used in forward(). A follow-up commit prefixes last_error with _ to resolve the ruff F841 warning and reformats the file.

Written by Mendral for commit c238a3a.

The retry loop in forward_incremental was catching all exceptions with a
bare 'except Exception: pass', making it impossible to diagnose failures.
This collects error messages, logs them when verbose mode is on, and
reports failures to the tracer - matching the behavior of forward().
Copilot AI review requested due to automatic review settings May 7, 2026 14:06
@greptile-apps

greptile-apps Bot commented May 7, 2026

Copy link
Copy Markdown

PR author is not in the allowed authors list.

@coderabbitai

coderabbitai Bot commented May 7, 2026

Copy link
Copy Markdown
Contributor

Walkthrough

The RetryPipeWrapper.forward_incremental method now records per-attempt error messages, stores the last exception in _last_error, and conditionally logs a truncated error prefix on each failed attempt when verbose is enabled. If retries are exhausted it emits a tracer "failure" event containing nb_retries and collected error_msgs, optionally logs that it is returning the previous action list (when verbose), and returns a PossibleActionSpace built from previous_action_list as before.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The pull request title accurately reflects the main change: improving error handling in forward_incremental by capturing and logging exceptions instead of silently swallowing them.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Tip

💬 Introducing Slack Agent: The best way for teams to turn conversations into code.

Slack Agent is built on CodeRabbit's deep understanding of your code, so your team can collaborate across the entire SDLC without losing context.

  • Generate code and open pull requests
  • Plan features and break down work
  • Investigate incidents and troubleshoot customer tickets together
  • Automate recurring tasks and respond to alerts with triggers
  • Summarize progress and report instantly

Built for teams:

  • Shared memory across your entire org—no repeating context
  • Per-thread sandboxes to safely plan and execute work
  • Governance built-in—scoped access, auditability, and budget controls

One agent for your entire SDLC. Right inside Slack.

👉 Get started


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.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick comments (2)
packages/notte-browser/src/notte_browser/tagging/action/llm_taging/base.py (2)

112-113: ⚡ Quick win

last_error is assigned but never used after the retry loop.

Unlike forward(), which passes last_error as the from cause in raise LLMParsingError(...) from last_error, forward_incremental only returns a fallback and never references last_error again. The variable is dead code.

🧹 Proposed fix — remove the unused variable
-        errors: list[str] = []
-        last_error: Exception | None = None
+        errors: list[str] = []
             except Exception as e:
-                last_error = e
-                errors.append(str(e))
+                errors.append(str(e))
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/notte-browser/src/notte_browser/tagging/action/llm_taging/base.py`
around lines 112 - 113, The variable last_error in the retry setup is declared
but never used in forward_incremental (unlike forward which uses it in raise
LLMParsingError from last_error); remove the dead variable to clean up the code:
delete the declaration "last_error: Exception | None = None" and any subsequent
unused references in the forward_incremental logic (leave the errors list and
fallback behavior intact), ensuring only meaningful error aggregation (errors
list) remains and behavior of forward_incremental is unchanged.

117-121: ⚡ Quick win

Missing ContextSizeTooLargeError early-exit that forward() has.

forward() short-circuits on context-too-large errors to avoid pointless retries. forward_incremental will retry the same call max_tries times for this error before falling back — wasting LLM tokens and time with zero chance of success.

🛡️ Proposed fix — mirror the early-exit from `forward()`
             except Exception as e:
-                last_error = e
                 errors.append(str(e))
+                if "Please reduce the length of the messages or completions" in str(e):
+                    pattern = r"Current length is (\d+) while limit is (\d+)"
+                    match = re.search(pattern, str(e))
+                    size = int(match.group(1)) if match else None
+                    max_size = int(match.group(2)) if match else None
+                    raise ContextSizeTooLargeError(size=size, max_size=max_size) from e
                 if self.verbose:
                     logger.debug(...)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/notte-browser/src/notte_browser/tagging/action/llm_taging/base.py`
around lines 117 - 121, forward_incremental currently catches all exceptions and
keeps retrying, but should mirror forward() by short-circuiting on
ContextSizeTooLargeError to avoid pointless retries; update the except block in
forward_incremental to detect ContextSizeTooLargeError (the same exception class
used in forward()), set last_error/errors as before, and immediately raise or
return early (i.e. no retry loop) when the caught exception is a
ContextSizeTooLargeError, preserving verbose logging behavior.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@packages/notte-browser/src/notte_browser/tagging/action/llm_taging/base.py`:
- Around line 112-113: The variable last_error in the retry setup is declared
but never used in forward_incremental (unlike forward which uses it in raise
LLMParsingError from last_error); remove the dead variable to clean up the code:
delete the declaration "last_error: Exception | None = None" and any subsequent
unused references in the forward_incremental logic (leave the errors list and
fallback behavior intact), ensuring only meaningful error aggregation (errors
list) remains and behavior of forward_incremental is unchanged.
- Around line 117-121: forward_incremental currently catches all exceptions and
keeps retrying, but should mirror forward() by short-circuiting on
ContextSizeTooLargeError to avoid pointless retries; update the except block in
forward_incremental to detect ContextSizeTooLargeError (the same exception class
used in forward()), set last_error/errors as before, and immediately raise or
return early (i.e. no retry loop) when the caught exception is a
ContextSizeTooLargeError, preserving verbose logging behavior.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: bd741858-1cfa-423a-8818-746005768e6c

📥 Commits

Reviewing files that changed from the base of the PR and between f7ecf75 and 672bd97.

📒 Files selected for processing (1)
  • packages/notte-browser/src/notte_browser/tagging/action/llm_taging/base.py

@2027-evals

2027-evals Bot commented May 7, 2026

Copy link
Copy Markdown

⚠️ Couldn't find a preview deployment for commit 6555c9e after 10 minutes.

2027 auto-runs evals against preview deployments of your docs. To enable this, install one of:

  • Mintlify — if you use Mintlify docs
  • Vercel — for Next.js / static sites
  • Netlify — for most static docs

Once a preview is deployed, open a new PR and we'll run the eval automatically.


Evaluating agent experience using 2027.dev · View dashboard

mendral-app[bot]

This comment was marked as outdated.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Copilot encountered an error and was unable to review this pull request. You can try again by re-requesting a review.

Fixes F841 (unused variable) in forward_incremental and reformats
base.py to pass pre-commit ruff hooks.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/notte-browser/src/notte_browser/tagging/action/llm_taging/base.py`:
- Line 113: The variable _last_error is unused dead code; remove its declaration
and any assignments to it (the class-level declaration "_last_error: Exception |
None = None" and the update inside the forward_incremental implementation) so
that only live variables remain; search for "_last_error" and delete its
definition and any lines that set it within the method(s) (e.g.,
forward_incremental) and run linters/tests to confirm no references remain.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 74d0a1c1-8190-4396-9088-aa7d84beb318

📥 Commits

Reviewing files that changed from the base of the PR and between 672bd97 and c238a3a.

📒 Files selected for processing (1)
  • packages/notte-browser/src/notte_browser/tagging/action/llm_taging/base.py

previous_action_list: list[InteractionAction],
) -> PossibleActionSpace:
errors: list[str] = []
_last_error: Exception | None = None

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion | 🟠 Major | ⚡ Quick win

_last_error is dead code — remove it entirely.

The variable is assigned on line 113 and updated on line 118, but never read. Unlike forward(), forward_incremental does not re-raise on exhaustion, so there is no consumer for this value. The underscore prefix suppresses the lint warning (F841) but the assignment itself still adds noise.

♻️ Proposed fix
         errors: list[str] = []
-        _last_error: Exception | None = None
         for _ in range(self.max_tries):
             try:
                 return await self.pipe.forward_incremental(snapshot, previous_action_list)
             except Exception as e:
-                _last_error = e
                 errors.append(str(e))
                 if self.verbose:

Also applies to: 118-118

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/notte-browser/src/notte_browser/tagging/action/llm_taging/base.py`
at line 113, The variable _last_error is unused dead code; remove its
declaration and any assignments to it (the class-level declaration "_last_error:
Exception | None = None" and the update inside the forward_incremental
implementation) so that only live variables remain; search for "_last_error" and
delete its definition and any lines that set it within the method(s) (e.g.,
forward_incremental) and run linters/tests to confirm no references remain.

@mendral-app mendral-app Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

LGTM

Previous feedback was addressed: last_error was renamed to _last_error, resolving the F841 linter error. The CI failure (Run unit tests) is caused by integration tests for personas/vault/sessions that depend on external API limits — unrelated to this PR's changes, and consistent with known flaky patterns in this repo. The code changes themselves are correct.

Tag @mendral-app with feedback or questions. View session

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