Summary
- A newly created profile succeeded in its first Edge process and reproduced the failure after reuse.
- Headed and headless runs both reproduced the crash.
- The failure remained when
download.save_as()was replaced and when Playwright’s Download API was removed later in the investigation. - Simplifying launch arguments and changing download behavior did not remove the pipe-path failure.
Test method
The comparison rule was simple: change one condition, keep the remaining inputs fixed, and record both successful and failed runs.
The main candidates were:
- accumulated state in the original profile;
- behavior unique to headless mode;
- the
Download.save_as()API; - a specific download behavior or launch argument.
New profile reuse
The first crash had occurred with a profile that had been used previously. To remove that history, Edge was started with an empty, test-only user-data directory.
During the first Edge process, all five downloads completed:
new profile / launch 1 / download 1 -> success
new profile / launch 1 / download 2 -> success
new profile / launch 1 / download 3 -> success
new profile / launch 1 / download 4 -> success
new profile / launch 1 / download 5 -> success
After Edge closed normally, the same profile was used again:
same profile / launch 1 -> success
same profile / launch 2 or 3 -> native crash during download
The pattern appeared across multiple independently created profiles. That ruled against corruption limited to the original personal profile. Reuse across Edge processes remained an important condition.
Headed and headless execution
Headless browser execution can differ from a normal visible browser, so both modes were tested.
| Mode | Observed result |
|---|---|
| Headless | First launch succeeded; a later launch disconnected during download |
| Headed | First launch succeeded; a later launch disconnected during download |
Crash dumps collected later from both modes pointed to the same instruction offset within msedge.dll. Headless mode was not required for the failure.
Removing save_as()
The original exception named Download.save_as(), making it a natural suspect. The download workflow was repeated with different completion paths.
| Test | Observed result |
|---|---|
Save with Download.save_as() |
Edge exits; Playwright reports TargetClosedError |
Read the temporary file through download.path() |
Same native crash |
| Send raw CDP commands without Playwright’s Download API | Same native crash |
The observed order explains why save_as() appeared in the error:
1. Edge encounters a native failure
2. The Edge process exits
3. The CDP connection closes
4. Playwright marks browser objects as closed
5. save_as() reports TargetClosedError
save_as() observed the closed process; it was not required to cause the crash.
Download behavior
Playwright 1.62.0 uses behavior similar to allowAndName when configuring downloads. A simpler allow behavior was also tested.
pipe + allow -> reproduces
pipe + allowAndName -> reproduces
port + allowAndName -> successful in the observed runs
Filename handling alone did not explain the result.
Launch arguments
Playwright adds browser arguments for automation stability. Candidate arguments were removed in stages, including feature-disable lists and Edge-specific update or compatibility switches.
The crash remained with a reduced pipe launch. The launch on which it appeared sometimes shifted from the second to the third run, but the failure did not disappear. This does not prove that every argument is irrelevant; it shows that no single obvious default argument explained the observed pattern.
Results
| Hypothesis | Result | Basis |
|---|---|---|
| Only the original profile was corrupt | Not supported | New profiles reproduced after reuse |
| Headless mode was required | Excluded | Headed mode also reproduced |
save_as() was required |
Excluded | The crash reproduced without the Download API |
allowAndName alone caused the crash |
Not supported | allow also reproduced over pipe |
| One obvious default launch argument caused it | Not supported | Reduced pipe launches still reproduced |
The remaining question was whether Playwright itself was required. The next step replaced the automation library with direct CDP communication from Node.js standard modules.