When keywords are called directly from Python, without a Robot Framework run, outputdir is the relative string "." (Browser/browser.py:455). Artefacts whose paths are handed to the Node side as relative strings are then resolved against the Node process's working directory, which is the wrapper directory — so they land inside the installed package instead of the user's working directory.
Under a robot run this cannot happen: outputdir comes from ${OUTPUTDIR} and is absolute. Verified.
Reproduce
from Browser import Browser
browser = Browser()
browser.new_browser("webkit", headless=True)
browser.new_page("https://robotframework-browser.org")
print(browser.take_screenshot())
browser.close_browser("ALL")
Observed:
browser/screenshot/robotframework-browser-screenshot-1.png <- returned path
- The returned path is relative and does not exist relative to the caller's working directory.
- An empty
./browser/screenshot/ is created in the caller's working directory by the Python side.
- The actual PNG is written to
<site-packages>/Browser/wrapper/browser/screenshot/robotframework-browser-screenshot-1.png.
The rule
Anything Python resolves to an absolute path before sending it over gRPC is fine. Anything sent as a relative string is resolved by Node against Browser/wrapper.
Affected
| Artefact |
Source |
| Screenshots |
returned path is relative and wrong |
tracing=True trace zip |
Browser/keywords/playwright_state.py:874-877 — no .resolve() |
Trace temp/<uuid>/ working files |
Browser/keywords/playwright_state.py:911 |
Save Storage State |
Browser/keywords/playwright_state.py:1720-1722 — from source, not run |
| Node V8 coverage output |
Browser/playwright.py:96 — from source, not run |
Not affected
| Artefact |
Why |
playwright-log.txt |
opened by Python itself, Browser/playwright.py:234-235 |
| Video |
explicitly resolved, Browser/keywords/playwright_state.py:948-960 |
An explicit tracing=Path("x.zip") is also fine — lines 878-880 do resolve it. Only the tracing=True path is affected.
Cause
Browser/browser.py:455 sets _output_dir = ".", and Browser/browser.py:795-798 returns it unchanged when there is no execution context. The Node process is started with cwd=self._browser_wrapper_dir (Browser/playwright.py:280, passed to Popen at :104).
Possible fix
Making the default absolute at construction time was probed in-process (Browser._output_dir = str(Path.cwd()) before constructing). It fixes the screenshot path, the trace zip and the trace temp directory, makes the returned paths absolute, and moves nothing to a new wrong place. It does not fix recordHar, which never passes through outputdir at all — filed separately.
Environment
Browser 20.3.0, Robot Framework 7.4.1, Python 3.14.7, macOS, webkit.
When keywords are called directly from Python, without a Robot Framework run,
outputdiris the relative string"."(Browser/browser.py:455). Artefacts whose paths are handed to the Node side as relative strings are then resolved against the Node process's working directory, which is the wrapper directory — so they land inside the installed package instead of the user's working directory.Under a
robotrun this cannot happen:outputdircomes from${OUTPUTDIR}and is absolute. Verified.Reproduce
Observed:
./browser/screenshot/is created in the caller's working directory by the Python side.<site-packages>/Browser/wrapper/browser/screenshot/robotframework-browser-screenshot-1.png.The rule
Anything Python resolves to an absolute path before sending it over gRPC is fine. Anything sent as a relative string is resolved by Node against
Browser/wrapper.Affected
tracing=Truetrace zipBrowser/keywords/playwright_state.py:874-877— no.resolve()temp/<uuid>/working filesBrowser/keywords/playwright_state.py:911Save Storage StateBrowser/keywords/playwright_state.py:1720-1722— from source, not runBrowser/playwright.py:96— from source, not runNot affected
playwright-log.txtBrowser/playwright.py:234-235Browser/keywords/playwright_state.py:948-960An explicit
tracing=Path("x.zip")is also fine — lines 878-880 do resolve it. Only thetracing=Truepath is affected.Cause
Browser/browser.py:455sets_output_dir = ".", andBrowser/browser.py:795-798returns it unchanged when there is no execution context. The Node process is started withcwd=self._browser_wrapper_dir(Browser/playwright.py:280, passed toPopenat:104).Possible fix
Making the default absolute at construction time was probed in-process (
Browser._output_dir = str(Path.cwd())before constructing). It fixes the screenshot path, the trace zip and the trace temp directory, makes the returned paths absolute, and moves nothing to a new wrong place. It does not fixrecordHar, which never passes throughoutputdirat all — filed separately.Environment
Browser 20.3.0, Robot Framework 7.4.1, Python 3.14.7, macOS, webkit.