Sessions & Auth
Sessions & Persistence
Log in once and every later run starts logged in; give each agent its own
isolated browser so they never fight over tabs. This page covers three things:
persisting session state to reuse it across runs, running multiple browsers in parallel with
--session, and cleanly resetting the session daemon when it gets stuck.
state save / --state / --session-name are for the browsers chrome-use
launches itself and for --session isolated browsers — those
start with no login, so a state file is how you carry it across runs. Driving your real Chrome
over the extension connection needs none of this: the login already lives in your real
profile and persists natively, surviving restarts. See Real Chrome.
Persist login state across runs
Log in once, save cookies and localStorage to a file, then load it on later runs to start already
logged in — no need to walk through the login form every time. Save with state save; pass
--state at launch, or use state load to inject the state into the current
session and keep going:
# Log in once, save cookies + localStorage + login state
$ chrome-use state save ./auth.json
# Pass the state at launch — start already logged in
$ chrome-use --state ./auth.json open https://app.example.com
# Or inject saved state into the current session and keep going
$ chrome-use state load ./auth.json
$ chrome-use open https://app.example.com/dashboard
For how to log in cleanly first and then save the session, see
Login & Credentials — that page covers the same state save
from the login-flow angle.
The state file is a plain JSON document with cookies, both kinds of storage, and an origins list:
{
"cookies": [...],
"localStorage": {...},
"sessionStorage": {...},
"origins": [...]
}
auth.json holds login-capable cookies and tokens — don't commit it to Git
(echo "*.auth-state.json" >> .gitignore), delete it when you're done, and keep it out of
any directory that gets packaged and distributed.
Automatic save/restore: --session-name
If you'd rather not save/load by hand, use --session-name (or the environment variable
AGENT_BROWSER_SESSION_NAME) — state for a same-named session is saved automatically and
restored automatically on later runs:
$ AGENT_BROWSER_SESSION_NAME=my-app chrome-use open https://app.example.com
# Run again with the same name later and state is restored automatically
cf_clearance cookie you get from passing a Cloudflare challenge is bound to your
IP + User-Agent. For an isolated session, the --session-name save/restore carries it
along, so as long as the egress IP and UA don't change, you skip the repeat challenge.
For more on login and credentials, see Login & Credentials.
Run multiple browsers in parallel with --session
Each --session <name> is a mutually isolated browser — its own
cookies, LocalStorage / SessionStorage, IndexedDB, cache, history, and tabs. Great for testing
multi-user flows or scraping in parallel:
$ chrome-use --session a open https://app.example.com
$ chrome-use --session b open https://app.example.com
$ chrome-use --session a fill @e1 "alice@test.com"
$ chrome-use --session b fill @e1 "bob@test.com"
To set a default session for the current shell, use AGENT_BROWSER_SESSION=myapp; when you
omit --session, commands land on the default session. Close a session with close
when you no longer need it, and use session list to see which sessions are still active:
$ AGENT_BROWSER_SESSION=myapp chrome-use open https://example.com
# Close a specific session
$ chrome-use --session auth close
# List active sessions
$ chrome-use session list
eval/screenshot off target). Two agents sharing
the same session (e.g. both on the bare default session) share one daemon and one
active tab, and overwrite each other. Give each agent its own --session <name>.
--session gets its own colored Chrome tab group, and sessions never touch each other's
tabs. Bare --cdp <port> is not isolated — every session attaches to the
same browser's existing targets, and a second session's first open can navigate a sibling
session's tab. To run concurrent agents on the same real Chrome, use the extension (each with a distinct
--session), not bare --cdp. See Real Chrome.
Grab a specific tab across sessions: targetId adoption
Each session has its own tab group with its own t<N> numbering (the same physical tab
is t8 in session A and t1 in session B), so t<N> is
not a stable handle across sessions. To grab a specific tab from another session
(say a session half-filled a form but its handle died), use the stable CDP targetId:
# Re-sync the live tabs, printing target: <id> on each line
$ chrome-use tab list --full --session B
# Adopt that exact tab — no reload, state preserved
$ chrome-use tab <targetId> --session B
tab list rediscovers the live tab set on every call, so a new session can see tabs opened by
other sessions (and re-attached tabs), not just its own. Adopting by targetId lands session B
on that stranded tab without reloading it, so the half-filled form survives.
open the same entry URL again, add --reuse-tab: if a tab
is already showing that URL (matched by origin+path), it switches to it instead of stacking up a new tab.
Reset a stuck daemon state
Each session runs a background daemon worker to hold the page handles. This daemon is also where the boa
JS engine lives — the engine runs outside the page, which is why
script survives whole-page hard navigations without losing
state. If a session starts misbehaving —
commands hitting the wrong tab, refs/handles looking stale, or a stale worker left over because you
upgraded chrome-use mid-session — just restart the daemon; don't go hunting for the PID with
pgrep/kill:
# List running session daemons (with relay status)
$ chrome-use daemon status
# Kill all session daemon workers
$ chrome-use daemon restart
daemon restart doesn't touch the extension's native messaging bridge (__nm-host),
so the relay to your live Chrome stays online — the next command just spins up a clean new daemon against
the same browser. It does not close any tab.
Rare case: hand a session to the user (session handoff / resume)
bwu get … | chrome-use fill … --stdin), read TOTP/2FA codes, drive OAuth, and persist the session (state save / --session-name) so later runs start logged in. Don't hand a login to the user just because it has a password or a 2FA step — solve it. See Login & Credentials.
session handoff is an escape hatch, not a login method. Use it only when a step is genuinely impossible for the agent — an image/behavioral captcha you can't solve, an SMS/authenticator code you have no access to, a hardware-key tap, a bank's "approve on your phone" prompt. Try autonomously first; hand off only as a last resort:
$ chrome-use session handoff # last resort: mark user-owned; tell the user exactly what to do
# … the human does the one thing the agent truly can't …
$ chrome-use session resume # take control back — ONLY after they confirm they're done
session resume line), so the agent can't fight the user for the tab. Zero-impact until you call handoff — the agent owns and drives every session by default, autonomous login included.
session status shows the owner; session list lists every session with its owner. Don't resume on your own to grab control back — wait for the user to say they're done.
Related pages
The auth credential store, OAuth, passwords piped from stdin — log in securely, then save the session.
The extension connection path: reuse your already-logged-in session, each --session getting its own colored tab group.
Set up chrome-use, the extension, and the agent skill to get the session flows above running.