Both claude.ai session URLs bounced to claude.ai/logout?involuntary=1 before I had typed anything, and the login page carried returnTo=%2Fcode%2Fsession_... so the redirect was clearly deliberate.

The setup was a pool of 25 Chrome for Testing slots (150.0.7871.24), each with its own --user-data-dir and debug port, so agent sessions could drive browsers without touching each other. I had pointed two remote-control session URLs at that pool. A stable Chrome 151 profile on port 9222 sat next to it, mostly unused for this.

Reading the bounce as a bot block

Chrome for Testing announces itself. It paints an “only for automated testing” banner on every window and sets automation signals. So I read the bounce as bot detection and went after the banner and the fingerprint.

I added --test-type to the launch flags, which is supposed to suppress the banner. It did not. The banner stayed on every window. I also spent a stretch reading up on undetected-browser tooling, and it produced a brief recommending nodriver for unattended work. That was a fix aimed at a problem the browser did not have.

The signal that broke my theory was that the magic link worked. I requested a sign-in link, clicked it, and the page really did sign in. Then /code and /new both kicked me back to logout. A bot wall would have stopped me at the door. This let me in and evicted me one navigation later.

The device key nobody mentioned

The kick-out reason was device_key_missing: an elevated-auth check on the Claude Code surfaces. Plain claude.ai auth is satisfied by the cookie. /code and /new additionally want a device key, and Chrome for Testing could not provide one. Every time I measured it, the sequence was the same:

  1. Magic link signs in.
  2. /code or /new loads.
  3. Redirect to logout?involuntary=1.

I measured it twice on the same day, to make sure I was not looking at a stale cookie.

Then I ran the same test in the stable Chrome on 9222 (151.0.7922.169, profile chrome-claude). I navigated to a session URL from a cold profile and got the login page with returnTo intact. I filled the email field through CDP, submitted, and signed in with the magic link. It held. /code loaded and stayed loaded.

One detail cost me a retry: my first attempt to read the page afterward threw SyntaxError: Invalid regular expression: missing /, because the JS I passed to Runtime.evaluate had its backslashes eaten by the shell heredoc. That was my quoting, not the page, and I fixed it before reading the result.

Moving claude.ai to one long-lived profile

We wrote the rule into the repo’s CLAUDE.md so the next agent does not rediscover it:

  • claude.ai work goes to stable Chrome, profile <state>/chrome-claude, debug port 9222.
  • It is opened as --app= windows, which have no tab strip and no omnibox.
  • One profile signed in once, many windows. Each session window is another --app window on the same running process, so it inherits the cookies and never signs in again.
  • The Chrome for Testing pool stays for everything else, and stays off claude.ai.

Chrome’s singleton lock has a catch. --window-size and --window-position are dropped for a second window on a live profile, so placement happens afterward through our desk tool rather than at launch.

Signing in also stopped needing a person. A mail rule on anthropic.com senders forwards the magic link, and the agent completes the login itself.

There was also a related bug in how we tell whose window is whose. Ownership was decided from the Chrome binary, but the agent’s main Chrome runs the same binary as mine against a different profile. Every window it owned was filed as mine, and no automation would touch it. It now resolves on the profile.

The pool was built for isolation and disposable state, and that is exactly what an elevated-auth check punishes. A browser that forgets who it is on purpose cannot hold an identity that has to be remembered.