Skip to content

test(browser): make signup email test fail-fast and accept login flow#801

Open
leo-notte wants to merge 5 commits into
mainfrom
leo/test-signup-email-prompt
Open

test(browser): make signup email test fail-fast and accept login flow#801
leo-notte wants to merge 5 commits into
mainfrom
leo/test-signup-email-prompt

Conversation

@leo-notte
Copy link
Copy Markdown
Contributor

@leo-notte leo-notte commented May 7, 2026

Summary

  • Drop @pytest.mark.flaky(reruns=3, reruns_delay=5) on test_signup_email_extraction so the test fails on the first bad run instead of silently retrying up to 3 more times (each rerun is a full 15-step agent run).
  • Rework the agent prompt to:
    • accept either signup OR login (the persona's account often already exists),
    • explicitly forbid Google / GitHub / SSO and force the plain email flow,
    • accept any logged-in landing page as success — the 'One more second' interstitial is no longer always shown, which was causing the validator to reject otherwise-successful runs.

Test plan

  • uv run pytest tests/browser/test_tools.py::test_signup_email_extraction -v passes against a working persona
  • Failing run actually fails (no silent flaky retries)
  • Agent never selects Google/GitHub buttons on console.notte.cc

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Tests

    • Enforced email-only signup/login flow; social/SSO options disallowed.
    • Requires using the persona inbox to open verification/magic-link emails; success is reaching any authenticated console page.
    • Added a stop-on-auth rule to avoid completing onboarding forms.
    • Introduced a 120s execution timeout while preserving flaky retry behavior.
  • Chores

    • CI now uploads signup debug logs artifact on every run with a 7-day retention (missing files ignored).

Note

Hardens test_signup_email_extraction with an explicit forbidden-button list (Google, GitHub, SSO, Microsoft, Apple, social), whitelists acceptable email-flow button labels, adds a recovery clause for accidental forbidden clicks, bumps the per-attempt timeout to 120s, restores @pytest.mark.flaky(reruns=3, reruns_delay=5), and adds file-based debug logging that survives xdist worker crashes.

Written by Mendral for commit 9b8dc75.

Drop the @pytest.mark.flaky(reruns=3) wrapper on
test_signup_email_extraction so a failed run fails the test instead of
silently retrying up to 3 more times. Rework the agent prompt to:
- accept either signup or login (existing accounts no longer fail),
- forbid Google/GitHub/SSO and force the plain email flow,
- accept any logged-in landing page as success (the 'One more second'
  interstitial is no longer always shown, which was causing validator
  rejections).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 7, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

Walkthrough

A CI workflow step was added to upload signup debug logs from /tmp/signup-debug/ as an artifact named signup-debug-logs, retained for 7 days and allowed to be missing. In tests, test_signup_email_extraction in tests/browser/test_tools.py gained a pytest.mark.timeout(120) decorator and its agent.run instruction string was replaced with a more detailed prompt that allows signup or login but restricts authentication to plain email/magic-link only (explicitly forbids social/SSO buttons with fail-and-restart on click), requires using the persona inbox for verification emails, defines success as landing on any authenticated console page, and instructs stopping immediately once authenticated.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 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 title clearly and specifically summarizes the two main changes: making the test fail-fast and accepting login flow alongside signup.
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 docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch leo/test-signup-email-prompt

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.

Copy link
Copy Markdown
Contributor

@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.

🧹 Nitpick comments (1)
tests/browser/test_tools.py (1)

55-73: 💤 Low value

LGTM — the fail-fast rework is clean.

The removal of @pytest.mark.flaky and the reworked prompt look correct. One small note on the prompt's stop condition:

"CRITICAL: do not fill in any onboarding form — stop immediately once authenticated."

If the first post-authentication page is the onboarding form (i.e., there is no intermediate logged-in page before it), an agent following this instruction literally would stop on the onboarding form and call it success, which matches the intent — but the phrasing "do not fill in" could be misread as "skip past it somehow." A slightly cleaner phrasing would be:

"CRITICAL: do not fill in or interact with any onboarding form — landing on any page inside the console (including an onboarding form) counts as success. Stop immediately."

This is purely cosmetic/clarifying and does not affect the current functional behavior.

🤖 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 `@tests/browser/test_tools.py` around lines 55 - 73, Update the prompt string
in test_signup_email_extraction (inside the agent.run call) to clarify the stop
condition: replace the sentence "CRITICAL: do not fill in any onboarding form —
stop immediately once authenticated." with wording like "CRITICAL: do not fill
in or interact with any onboarding form — landing on any page inside the console
(including an onboarding form) counts as success. Stop immediately." so the
agent won't misinterpret "do not fill in" as needing to skip past an onboarding
page; keep this change confined to the task string passed to agent.run in the
test.
🤖 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 `@tests/browser/test_tools.py`:
- Around line 55-73: Update the prompt string in test_signup_email_extraction
(inside the agent.run call) to clarify the stop condition: replace the sentence
"CRITICAL: do not fill in any onboarding form — stop immediately once
authenticated." with wording like "CRITICAL: do not fill in or interact with any
onboarding form — landing on any page inside the console (including an
onboarding form) counts as success. Stop immediately." so the agent won't
misinterpret "do not fill in" as needing to skip past an onboarding page; keep
this change confined to the task string passed to agent.run in the test.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: b49479a1-8028-471d-beb8-622ed5278568

📥 Commits

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

📒 Files selected for processing (1)
  • tests/browser/test_tools.py

@greptile-apps
Copy link
Copy Markdown

greptile-apps Bot commented May 7, 2026

Greptile Summary

This PR removes the @pytest.mark.flaky retry decorator from test_signup_email_extraction and rewrites the agent task prompt to handle both the signup and login paths, accept any logged-in landing page as success, and explicitly forbid social/SSO sign-in options.

  • Dropping the three retries is a correctness improvement — silent reruns were masking real failures at the cost of three full 15-step agent runs per flap.
  • The prompt rewrite broadens the success condition to match real-world behavior (the 'One more second' interstitial is not always shown), while adding explicit guardrails against Google/GitHub buttons.
  • A side effect of accepting the login path is that the persona's email-inbox tool may never be invoked on repeat runs, so the "email extraction" half of the test name is no longer reliably exercised.

Confidence Score: 4/5

Safe to merge — the change is test-only and the core fix (removing silent retries) is straightforward and correct.

The flaky-retry removal and prompt rewrite are well-reasoned, but accepting the login path means the email-inbox reading tool may never be invoked after the account is first created, quietly narrowing what the test actually validates on every subsequent run.

tests/browser/test_tools.py — specifically whether the email-extraction tool path is still reachable after the account has been created once.

Important Files Changed

Filename Overview
tests/browser/test_tools.py Removed flaky retry decorator and rewrote agent task prompt to accept login or signup path; resp.success check is unchanged but email extraction is no longer exercised when the login path is taken

Comments Outside Diff (1)

  1. tests/browser/test_tools.py, line 55-73 (link)

    P2 Test name no longer matches actual coverage on repeat runs

    The test is called test_signup_email_extraction, but its core value was exercising the persona's email-inbox tool (reading the verification/magic-link email). Now that the prompt accepts the login path, a persona whose account already exists will authenticate without any email extraction at all — the inbox tool is never invoked, and resp.success can pass entirely on the password/session login branch. On every run after the first, the "email extraction" capability goes untested. Consider either renaming the test to reflect its new scope (test_authenticate_email_flow) or adding an assertion that confirms the inbox tool was called when the signup path was taken.

    Prompt To Fix With AI
    This is a comment left during a code review.
    Path: tests/browser/test_tools.py
    Line: 55-73
    
    Comment:
    **Test name no longer matches actual coverage on repeat runs**
    
    The test is called `test_signup_email_extraction`, but its core value was exercising the persona's email-inbox tool (reading the verification/magic-link email). Now that the prompt accepts the login path, a persona whose account already exists will authenticate without any email extraction at all — the inbox tool is never invoked, and `resp.success` can pass entirely on the password/session login branch. On every run after the first, the "email extraction" capability goes untested. Consider either renaming the test to reflect its new scope (`test_authenticate_email_flow`) or adding an assertion that confirms the inbox tool was called when the signup path was taken.
    
    How can I resolve this? If you propose a fix, please make it concise.

    Fix in Claude Code

Fix All in Claude Code

Prompt To Fix All With AI
Fix the following 1 code review issue. Work through them one at a time, proposing concise fixes.

---

### Issue 1 of 1
tests/browser/test_tools.py:55-73
**Test name no longer matches actual coverage on repeat runs**

The test is called `test_signup_email_extraction`, but its core value was exercising the persona's email-inbox tool (reading the verification/magic-link email). Now that the prompt accepts the login path, a persona whose account already exists will authenticate without any email extraction at all — the inbox tool is never invoked, and `resp.success` can pass entirely on the password/session login branch. On every run after the first, the "email extraction" capability goes untested. Consider either renaming the test to reflect its new scope (`test_authenticate_email_flow`) or adding an assertion that confirms the inbox tool was called when the signup path was taken.

Reviews (1): Last reviewed commit: "test(browser): tighten signup email test..." | Re-trigger Greptile

Re-add @pytest.mark.flaky(reruns=3, reruns_delay=5) so transient agent
flakes don't fail the suite, but pin a 90s @pytest.mark.timeout per
attempt instead of relying on the global 300s. Each retry now fails
fast on a hang rather than burning the full 5-minute budget.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
mendral-app[bot]

This comment was marked as outdated.

Bump @pytest.mark.timeout from 90s to 120s. A clean run takes ~50s
locally, but variance from LLM rate-limit backoff (vertex_ai 429s
under concurrent xdist load) can easily double that. 120s gives a
safety margin while still well below the global 300s and capping
worst-case wall time across reruns at ~8 minutes.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
mendral-app[bot]

This comment was marked as outdated.

Replace the soft 'never use Google sign-in' instruction with an
ABSOLUTE RULE that enumerates the forbidden button labels (Google,
GitHub, SSO, Microsoft, Apple, social) and instructs the agent to
read each button's text BEFORE clicking. Also whitelist the
acceptable email-flow button labels and add an explicit recovery
clause if a forbidden button is clicked by accident.

Why: even when the agent's stated intent was 'click Send magic link',
the action mapper was sometimes resolving to id=B2 = 'Use Google',
landing on a Google OAuth flow the agent couldn't escape. The
soft prompt let it slip through; the explicit forbid-list does not.
9/9 local runs pass with the new prompt vs 1/3 previously.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
mendral-app[bot]

This comment was marked as outdated.

Add a dual loguru + stdlib FileHandler in test_signup_email_extraction
that writes to /tmp/signup-debug/signup-{ts}-{pid}.log, and an
actions/upload-artifact step that uploads that directory on every CI
run (if: always).

Why: in CI the test crashes its xdist worker at the pytest-timeout
('node down: Not properly terminated'), and the worker's captured
stdout dies with it — we have zero visibility into what the agent
or litellm were doing. Writing directly to a file (with
enqueue=False, immediate flush) means the log survives a SIGKILL.

Captures both notte's loguru output and litellm's stdlib logging
(rate-limit / 429 messages, etc.), so we can finally tell whether CI
crashes are 429 storms, action misclicks, or something else.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Copy link
Copy Markdown
Contributor

@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: 2

🧹 Nitpick comments (1)
tests/browser/test_tools.py (1)

67-71: ⚡ Quick win

Two independent file descriptors are writing to the same log_path, risking interleaved/garbled output.

logger.add(str(log_path), ...) opens one file descriptor; logging.FileHandler(str(log_path)) opens a second independent one to the same path. Writes from both can interleave mid-line, which defeats the debugging purpose.

Loguru supports passing a logging.Handler directly as a sink, so you can eliminate the second fd entirely and let loguru own the file:

✅ Proposed fix
-    loguru_sink_id = logger.add(str(log_path), level="DEBUG", enqueue=False, backtrace=True, diagnose=True)
-    stdlib_handler = logging.FileHandler(str(log_path))
-    stdlib_handler.setLevel(logging.DEBUG)
-    stdlib_handler.setFormatter(logging.Formatter("%(asctime)s [stdlib %(name)s %(levelname)s] %(message)s"))
-    logging.getLogger().addHandler(stdlib_handler)
+    stdlib_handler = logging.FileHandler(str(log_path))
+    stdlib_handler.setLevel(logging.DEBUG)
+    stdlib_handler.setFormatter(logging.Formatter("%(asctime)s [stdlib %(name)s %(levelname)s] %(message)s"))
+    # Use loguru as the sole writer; pass the stdlib FileHandler as a sink so
+    # both loguru and stdlib records flow through one fd without interleaving.
+    loguru_sink_id = logger.add(stdlib_handler, level="DEBUG", backtrace=True, diagnose=True)
+    logging.getLogger().addHandler(stdlib_handler)

The finally cleanup block remains correct as-is.

🤖 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 `@tests/browser/test_tools.py` around lines 67 - 71, Two independent file
descriptors are being opened for the same log_path (logger.add(str(log_path),
...) and logging.FileHandler(str(log_path))), which can interleave output; fix
by creating a single logging.Handler and passing that handler as the sink to
logger.add instead of opening the file twice: instantiate stdlib_handler =
logging.FileHandler(str(log_path)), setLevel and setFormatter on stdlib_handler,
then call logger.add(stdlib_handler, level="DEBUG", enqueue=False,
backtrace=True, diagnose=True) and remove the separate
logging.getLogger().addHandler(stdlib_handler) and the logger.add(str(log_path),
...) call so only one FD is used (references: logger.add, logging.FileHandler,
logging.getLogger().addHandler).
🤖 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 `@tests/browser/test_tools.py`:
- Around line 64-66: The current creation of log_dir via
Path(os.getenv("SIGNUP_DEBUG_LOG_DIR", "/tmp/signup-debug")) is flagged for
using a predictable /tmp path; replace the default with a safe temp-backed
directory: use tempfile.mkdtemp() or construct Path(tempfile.gettempdir()) /
"signup-debug" as the fallback when SIGNUP_DEBUG_LOG_DIR is not set, then ensure
the existing log_dir.mkdir(parents=True, exist_ok=True) and log_path creation
(the variables log_dir and log_path) continue to work unchanged so tests still
write to a valid directory.
- Around line 61-62: Remove the flaky reruns decorator so the test fails fast:
delete the line with `@pytest.mark.flaky`(reruns=3, reruns_delay=5) leaving only
`@pytest.mark.timeout`(120) above the test function (i.e., remove the
`@pytest.mark.flaky` decorator attached to the same test so it no longer performs
reruns).

---

Nitpick comments:
In `@tests/browser/test_tools.py`:
- Around line 67-71: Two independent file descriptors are being opened for the
same log_path (logger.add(str(log_path), ...) and
logging.FileHandler(str(log_path))), which can interleave output; fix by
creating a single logging.Handler and passing that handler as the sink to
logger.add instead of opening the file twice: instantiate stdlib_handler =
logging.FileHandler(str(log_path)), setLevel and setFormatter on stdlib_handler,
then call logger.add(stdlib_handler, level="DEBUG", enqueue=False,
backtrace=True, diagnose=True) and remove the separate
logging.getLogger().addHandler(stdlib_handler) and the logger.add(str(log_path),
...) call so only one FD is used (references: logger.add, logging.FileHandler,
logging.getLogger().addHandler).
🪄 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: b2d05379-d936-497d-8faf-f0f675a0c1a4

📥 Commits

Reviewing files that changed from the base of the PR and between 314faf9 and 9b8dc75.

📒 Files selected for processing (2)
  • .github/workflows/test-cicd.yml
  • tests/browser/test_tools.py

Comment on lines +61 to 62
@pytest.mark.timeout(120)
@pytest.mark.flaky(reruns=3, reruns_delay=5)
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.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

@pytest.mark.flaky was supposed to be removed by this PR.

The stated objective of this PR is to make the test fail-fast by removing @pytest.mark.flaky(reruns=3, reruns_delay=5), but the decorator is still present on Line 62. With @pytest.mark.timeout(120) added, the test can now spend up to 3 × (120 s + 5 s delay) = 375 s before reporting a hard failure — the opposite of the fail-fast intent described in the PR title and description.

🐛 Proposed fix
 `@pytest.mark.timeout`(120)
-@pytest.mark.flaky(reruns=3, reruns_delay=5)
 def test_signup_email_extraction(persona: NottePersona):
🤖 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 `@tests/browser/test_tools.py` around lines 61 - 62, Remove the flaky reruns
decorator so the test fails fast: delete the line with
`@pytest.mark.flaky`(reruns=3, reruns_delay=5) leaving only
`@pytest.mark.timeout`(120) above the test function (i.e., remove the
`@pytest.mark.flaky` decorator attached to the same test so it no longer performs
reruns).

Comment on lines +64 to +66
log_dir = Path(os.getenv("SIGNUP_DEBUG_LOG_DIR", "/tmp/signup-debug"))
log_dir.mkdir(parents=True, exist_ok=True)
log_path = log_dir / f"signup-{int(time.time() * 1000)}-{os.getpid()}.log"
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.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Ruff S108: predictable /tmp path could allow a symlink attack in shared environments.

In a typical single-tenant CI runner the risk is low, and the path is already overridable via SIGNUP_DEBUG_LOG_DIR. Using tempfile.mkdtemp() or Path(tempfile.gettempdir()) / "signup-debug" would silence the lint and is marginally safer.

🧰 Tools
🪛 Ruff (0.15.12)

[error] 64-64: Probable insecure usage of temporary file or directory: "/tmp/signup-debug"

(S108)

🤖 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 `@tests/browser/test_tools.py` around lines 64 - 66, The current creation of
log_dir via Path(os.getenv("SIGNUP_DEBUG_LOG_DIR", "/tmp/signup-debug")) is
flagged for using a predictable /tmp path; replace the default with a safe
temp-backed directory: use tempfile.mkdtemp() or construct
Path(tempfile.gettempdir()) / "signup-debug" as the fallback when
SIGNUP_DEBUG_LOG_DIR is not set, then ensure the existing
log_dir.mkdir(parents=True, exist_ok=True) and log_path creation (the variables
log_dir and log_path) continue to work unchanged so tests still write to a valid
directory.

Copy link
Copy Markdown

@mendral-app mendral-app Bot left a comment

Choose a reason for hiding this comment

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

Needs attention — 1 issue in 1 file

The new logging teardown has a bug: logger.remove(loguru_sink_id) raises ValueError: There is no existing handler with id 5 when the test is retried by @pytest.mark.flaky. This is confirmed in the CI failure for run 25514905384. The other 4 failures (vertex_ai rate limits, test_observe_with_instructions) are pre-existing integration flakiness tracked in insight 01KP9XGDY6FQENXZ0W8V6QK5H4 and not caused by this PR.

Prompt for AI agents (all issues)
Check if these issues are valid — if so, understand the root cause of each and fix them.

<assessment>
The new logging teardown has a bug: `logger.remove(loguru_sink_id)` raises `ValueError: There is no existing handler with id 5` when the test is retried by `@pytest.mark.flaky`. This is confirmed in the CI failure for run [25514905384](https://github.com/nottelabs/notte/actions/runs/25514905384). The other 4 failures (vertex_ai rate limits, `test_observe_with_instructions`) are pre-existing integration flakiness tracked in [insight 01KP9XGDY6FQENXZ0W8V6QK5H4](https://app.mendral.com/insights/01KP9XGDY6FQENXZ0W8V6QK5H4) and not caused by this PR.
</assessment>

<file name="tests/browser/test_tools.py">
<issue location="tests/browser/test_tools.py:107">
`logger.remove(loguru_sink_id)` raises `ValueError` on reruns because the sink ID from a previous attempt is no longer valid when the timeout fires mid-run and the `finally` block executes. Wrap the removal in a try/except to make teardown idempotent.
</issue>
</file>

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

Comment on lines +107 to +109
logger.remove(loguru_sink_id)
logging.getLogger().removeHandler(stdlib_handler)
stdlib_handler.close()
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

bug (P1): logger.remove(loguru_sink_id) raises ValueError on reruns because the sink ID from a previous attempt is no longer valid when the timeout fires mid-run and the finally block executes. Wrap the removal in a try/except to make teardown idempotent.

Suggested change
Suggested change
logger.remove(loguru_sink_id)
logging.getLogger().removeHandler(stdlib_handler)
stdlib_handler.close()
try:
logger.remove(loguru_sink_id)
except ValueError:
pass
logging.getLogger().removeHandler(stdlib_handler)
stdlib_handler.close()
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At tests/browser/test_tools.py, line 107:

<issue>
`logger.remove(loguru_sink_id)` raises `ValueError` on reruns because the sink ID from a previous attempt is no longer valid when the timeout fires mid-run and the `finally` block executes. Wrap the removal in a try/except to make teardown idempotent.
</issue>

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.

1 participant