playwright.chromium.launch_persistent_context() drops session cookies on browser closure when cookies lack an explicit expires or max-age attribute, causing the browser engine to classify them as in-memory session cookies that purge upon exit. This also occurs when scripts terminate abruptly via browser.close() or sys.exit() before Playwright flushes the user data directory’s SQLite cookie database to disk. Additionally, navigating across mismatched domain origins or failing to call context.close() prevents session state serialization.
Quick Fix
Explicitly call context.close()—not browser.close()—and manually extract/re-inject session cookies while setting an explicit future expiration timestamp:
# Force-convert session cookies to persistent cookies before context exit
cookies = context.cookies()
for cookie in cookies:
if "expires" not in cookie or cookie["expires"] == -1:
cookie["expires"] = 1800000000 # Set explicit future epoch timestamp
context.add_cookies(cookies)
context.close()
Step-by-Step Resolution
- Initialize
launch_persistent_contextwith an explicit absolute path to a non-volatile user data directory:
from pathlib import Path
from playwright.sync_api import sync_playwright
USER_DATA_DIR = Path("./user_data_dir").resolve()
- Implement explicit context lifecycle management to guarantee that memory-mapped cookie stores (
Network Persistent StateandCookiesSQLite databases) write to disk before script termination:
from pathlib import Path
from playwright.sync_api import sync_playwright
USER_DATA_DIR = Path("./user_data_dir").resolve()
with sync_playwright() as p:
context = p.chromium.launch_persistent_context(
user_data_dir=str(USER_DATA_DIR),
headless=True,
accept_downloads=True
)
page = context.new_page()
page.goto("https://example.com/login")
# Perform authentication actions...
# Extract and update session cookies that lack explicit expiration
cookies = context.cookies()
updated_cookies = []
for cookie in cookies:
if cookie.get("expires") == -1 or "expires" not in cookie:
# Assign a 1-year expiration to prevent session-purge on exit
cookie["expires"] = 1800000000
updated_cookies.append(cookie)
context.add_cookies(updated_cookies)
# CRITICAL: Close context explicitly to flush SQLite WAL buffers to disk
context.close()
- Verify directory persistence structure in your filesystem to confirm
user_data_dir/Default/Cookiesexists and is actively modified on context closure.
Why This Fails for Some Users
- Closing
browserInstead ofcontext: Callingbrowser.close()on a persistent context object bypasses Playwright’s graceful shutdown sequence for SQLite Write-Ahead Logging (WAL), leaving uncommitted transaction logs in temporary memory. - Strict SameSite/Host-Only Cookie Flags: Cookies set with
SameSite=Strictor host-only attributes without explicit domain scoping (.example.comvsexample.com) fail to rebind to subdomains across new execution runs. - Docker Mount and Permission Lockouts: Running inside containers without mounting
user_data_dirto a persistent volume, or running asrootinside an image where permissions restrict write access to/tmpor mapped paths, discards browser state upon container disposal. - Incognito/Ephemeral Context Overrides: Passing
no_viewport=Trueor combininglaunch_persistent_contextwith explicitcontext.new_page()creation in parallel workers can spawn temporary isolated contexts that do not inherit the parentuser_data_dirstate.