Skip to main content
Each entry starts with the message or value you see, then gives the cause and the fix. The Python package prints its warnings with an apostate: prefix and the Node package with [Apostate], and the browser prefixes its own refusals with apostate:. When nothing here matches, run the same launch in host mode to tell whether the persona or the host and network cause the problem, and read the limitations list of --fingerprint-explain.

Installing the browser

Cause. The package holds no digest for this host’s archive, and it could not fetch one from the GitHub release. The host has no route to github.com, or the release for this package version has no archive for this platform.Fix. Check that the host can reach github.com. apostate info lists the URLs it tried under manifest_urls_tried. On a host with no internet access, follow Offline installs.
The Node package says Apostate does not ship a binary for host darwin/x64; supported targets are linux-x64, linux-arm64, macos-arm64, windows-x64.Cause. The host is not one of the four supported platforms, for example an Intel Mac.Fix. Run on Linux x64, Linux arm64, macOS on Apple silicon or Windows x64. See Installation.
The Node package says SHA-256 mismatch for apostate-152.0.7977.83-<platform>.<ext>.Cause. The bytes downloaded are not the archive the manifest names. The download was cut short or damaged, a mirror set in APOSTATE_DOWNLOAD_BASE_URL serves other bytes, a network appliance rewrote the response, or --manifest names a different build.Fix. Run apostate install --force to download again. Unset APOSTATE_DOWNLOAD_BASE_URL to rule out the mirror. Nothing was extracted, so the cache is unchanged.
The Node package says this system cannot read a .tar.zst archive: install the zstd command line tool, or a tar new enough to accept --zstd.Cause. The Linux archives are zstd-compressed, and nothing on the host can read them.Fix. Install the zstd command, for example sudo apt install zstd, and run apostate install again.
Cause. The Node package’s first launch() was given a proxy and has no browser installed yet. It does not download the browser through the launch proxy.Fix. Run npx apostate install once without the proxy, then launch with it.
With binary_path, the Python package adds Omit binary_path to let the package find an existing install, or download and verify the release artifact. The Node package names the configured path.Cause. binary_path, executablePath or APOSTATE_BINARY points somewhere that is not the browser.Fix. Point it at the executable, a Chromium.app bundle, or the directory the archive unpacks to, such as apostate-152.0.7977.83-linux-x64/. Or unset it and let the package find or download the browser.
Cause. The well-known search passed it over. apostate info shows why:
The reasons are no Apostate payload beside it (build/MANIFEST.lock or resources/profiles/catalogue.json), reports Chromium <version>, not 152.0.7977.83, version could not be established and not executable.Fix. Move the whole extracted directory into a well-known location, not the browser alone: build/MANIFEST.lock and resources/profiles/ must sit beside it. Or name it with APOSTATE_BINARY. Installation lists the locations.
Cause. The script called Playwright’s or Patchright’s own launch(), which looks for Playwright’s Chromium, not Apostate.Fix. Import launch from apostate or @heretic-tech/apostate. If you drive the binary yourself, pass executable_path (Node: executablePath) with the path apostate path prints. Do not run playwright install; it downloads a stock Chromium that presents your real machine.
Cause. The Patchright package is missing or broken in this environment.Fix. Run the command in the message. In Node, run npm install patchright.
Cause. The Node package runs on Node 20, which lacks Promise.withResolvers. The package uses it for the GeoIP lookup, Linux archive extraction, Widevine provisioning, launchProcess() and the Xvfb display, so all of these fail on Node 20.Fix. Use Node 22 or later. The Node package and the MCP server both need it.

Launching

Cause. A headed launch (headless=False) on a Linux host with no display. The packages start a private Xvfb display for it and need the Xvfb program.Fix. Run sudo apt install xvfb, or launch headless. Linux servers has the details.
Cause. The browser was started headed on a Linux host with no display, through apostate run or the binary itself. The package launch functions and Node’s launchProcess() start Xvfb for a headed launch. The CLI and the binary do not.Fix. Add --headless=new, or run the browser under xvfb-run. It needs the xauth package as well as xvfb. Pass --no-first-run too, or a headed first launch of a new user data directory can stop at a first-run dialog:
Raw binary has the same command for the binary.
Cause. A headed first launch of a new user data directory without --no-first-run. apostate run and the binary add nothing to the switches you pass. In 0.4.3, Node’s launchProcess() did not pass it either, so its DevTools port never opened.Fix. Add --no-first-run and --no-default-browser-check to the switches. The package launch functions pass both, and from 0.4.4 so does launchProcess().
Cause. Chromium on Linux refuses to run as root with its sandbox on. Playwright and Patchright pass --no-sandbox by default, so this shows under Puppeteer, launchProcess(), apostate run and the binary itself, often in a container.Fix. Run as a regular user. If you must run as root, add --no-sandbox to the switches.
Cause. The browser refused a switch and exited. The browser’s log lines inside the error say which, after apostate:. Common refusals: a core count or memory size above the host’s, a GPU renderer string not in the launch’s GPU family, a screen smaller than --window-size, a seed with spaces or over 512 bytes, and a proxy credential the browser cannot parse.Fix. Change the value the apostate: line names. Switches lists what each switch accepts. With a proxy set, the Python package cuts the message at the proxy URL, which drops the apostate: line. Run the launch once without proxy to read it.With 0.4.3, the message can include the --apostate-profile switch, which holds the proxy credential base64-encoded. Remove it before you share the message. From 0.4.4 the packages replace it with <redacted>.
The Node package refuses userDataDir in launch() and --user-data-dir in args the same way.Cause. A user data directory makes a persistent context, and launch() returns a browser.Fix. Call launch_persistent_context(DIR, ...) (Node: launchPersistentContext(DIR, options)). See Seeds and identity.
The browser says apostate: --fingerprint names an off value, so no profile is composed and --fingerprint-platform cannot take effect.Cause. fingerprint="host" (or off, false, 0, disable, disabled) turns composition off, so a persona, a GPU family or a per-field switch has nothing to change.Fix. Drop one of the two: host mode for the real machine, or a seed and persona for a composed one.
Cause. A misspelt or unknown --fingerprint* switch in args. Chromium ignores unknown switches, so the packages refuse them, and also refuse a switch one or two letters away from a real one.Fix. Correct the spelling. Switches lists them all.
Cause. --fingerprint-noise takes no value. The browser only checks whether it is present, so any value turns noise on, and the packages refuse values that read as off.Fix. Leave the switch out to keep noise off.
Cause. A written profile and --fingerprint in args in one launch. A profile that describes a device replaces composition, so the seed would be ignored.Fix. Use one or the other. See Custom profiles.
Cause. The script called launch() where an asyncio loop is already running, such as in a Jupyter notebook. In 0.4.3 a second launch() in a thread that still has a browser open fails the same way.Fix. Use launch_async(), launch_context_async() or launch_persistent_context_async() with await. From 0.4.4, browsers launched in one thread share its driver, so several can be open at once. Python has both ways.
Cause. The text after the colon names what failed. Usually the package could neither copy the Widevine module from a local Google Chrome nor download it from Google’s update service, or it could not write the module beside the browser. The browser runs without DRM.Fix. Widevine covers installing the module yourself.

Proxies and network

Cause. The SOCKS5 proxy refused the connection or the credentials. When the credentials are wrong, the GeoIP lookup before launch also warns SOCKS5 proxy rejected the supplied credentials.Fix. Check the host, port, username and password. Percent-encode /, % and spaces in the credential as %2F, %25 and %20. Test the proxy outside the browser:
Cause. The HTTP proxy kept answering 407 Proxy Authentication Required, so the credentials were rejected. This is the error a page load gets against a proxy that refuses every credential.Fix. Check the username and password, encoded as above.
Cause. A SOCKS5 credential reached Playwright or Patchright directly, through their own proxy option in context_options or in a script that drives the binary itself. The drivers refuse it before the browser starts.Fix. Pass the proxy through the package’s proxy option, which hands the credential to the browser and not to the driver. When you drive the binary yourself, put the credential in the switch instead of the driver’s proxy option: --proxy-server=socks5://user:pass@proxy.example:1080. See Proxies.
Cause. socks5h:// is not a Chromium proxy scheme. The Node package refuses it with proxy must be a valid HTTP(S), SOCKS4, or SOCKS5 URL.Fix. Use socks5://. The browser already resolves host names on the proxy with it.
Cause. The Node package builds --proxy-server itself, splitting the endpoint from the credential.Fix. Pass proxy: "socks5://user:pass@proxy.example:1080" instead.
Cause. Before launch, the package asks ip-api.com, ipinfo.io, ipwho.is and ifconfig.co over plain HTTP, through the proxy, for the exit’s country and timezone. None answered within the time limit (20 seconds), because the proxy blocks port 80, rejected the credential, or the services were unreachable. The launch goes ahead with the host’s timezone and the persona’s default language, en-US. The Node package also puts the warning in browser.apostateDiagnostics.warnings, and from 0.4.4 the Python package puts it in browser.apostate_diagnostics["warnings"]. The 0.4.3 packages print an older wording, which says the launch keeps the host’s locale. The persona uses en-US in that case.Fix. Pass locale and timezone yourself when you know the exit’s location. Raise geoip_timeout (Node: geoipTimeoutMs) for slow proxies. See Locale and timezone.
Cause. A service answered but left out the timezone or the country. The field it could not fill stays the host’s timezone, or the persona’s default language.Fix. Pass the missing timezone or locale yourself.

Wrong values on a page

Cause. --lang has no effect on a persona. The persona’s language comes from --fingerprint-locale, which the packages set from the locale option or from GeoIP. Without either, it is en-US.Fix. Pass locale="de-DE" (Node: locale: "de-DE"), or --fingerprint-locale=de-DE to the binary.
Cause. No timezone reached the browser, because of geoip=False with no timezone, or a GeoIP lookup that failed. The browser then keeps the host’s timezone.Fix. Pass timezone="Europe/Berlin" or another IANA name.
Cause. The timezone option is not an IANA zone. From 0.4.4 the packages refuse it before launch, Python with this ConfigurationError and Node with a RangeError that says the same. The 0.4.3 packages passed it to the browser, and a page’s Intl.DateTimeFormat().resolvedOptions().timeZone read undefined.Fix. Pass an IANA name such as Europe/Berlin or America/New_York.
Cause. A persona shows only the fonts on its list that the host has installed, and nothing warns you. A Linux or macOS host has no Windows fonts until you install them.Fix. Run apostate fonts install windows. It needs git, and on Linux fc-cache from fontconfig, and it prints any core Windows family still missing. Fonts covers macOS personas and checking. Do not check with document.fonts.check(), which returns true for any family name. Measure text widths instead.
Cause. A persona never claims more cores or memory than the host has. On a smaller host, the seed’s draw runs over fewer options, and when none fits, the page reads the host’s own value.Fix. See the host cap for sizing, or pin both with --fingerprint-hardware-concurrency and --fingerprint-device-memory.
Cause. One of these changed: the persona (the default differs between hosts, and the identity file does not record it), the identity file (deleted, or replaced because it was not a valid seed), the host’s size, or the catalogue tables in a new release.Fix. Pass fingerprint_platform on every launch. Compare the seed and catalogue lines of --fingerprint-explain with a launch that looked right. Seeds and identity explains each.
Cause. Playwright and Patchright emulate a light colour scheme unless told not to, over the persona’s theme. The 0.4.3 packages did not turn that off. From 0.4.4 they do.Fix. With the 0.4.3 packages, pass color_scheme="null" in Python, or call await page.emulateMedia({ colorScheme: null }) on each page in Node. Known gaps has the details.
Cause. Headless host mode. With --fingerprint=host and no display, the page reads Chrome’s own headless screen.Fix. Use a seed, or launch headed.
Cause. Playwright’s default viewport emulation, in a script that drives the binary itself. The packages turn it off; Python Playwright treats viewport=None as the 1280x720 default.Fix. Pass no_viewport=True in Python or viewport: null in Node. See Screen and window.