Documents render_docx.py fails on Windows with LibreOffice temporary UserInstallation profile
Summary
The Documents plugin renderer render_docx.py fails on Windows when it launches LibreOffice with a temporary UserInstallation profile. Direct LibreOffice conversion of the same DOCX succeeds, and Poppler PDF-to-PNG conversion succeeds, so the failure appears tied to the temporary LibreOffice profile invocation used by the renderer.
Environment
- OS: Windows 10 Home
- Codex CLI:
codex-cli 0.130.0-alpha.5 - Codex Desktop App on Windows
- Documents plugin bundle/version:
openai-primary-runtime/documents/26.521.10419 - LibreOffice:
26.2.3.2 - Poppler /
pdftoppm: installed and usable
Observed behavior
Running the Documents skill renderer against a simple generated DOCX fails before producing the intermediate PDF/PNGs:
RuntimeError: Failed to produce PDF for rasterization (direct and ODT fallback).
--- DOCX->PDF ---
CMD: soffice -env:UserInstallation=file://C:\Users\<USER>\AppData\Local\Temp\soffice_profile_xxx --invisible --headless --norestore --convert-to pdf --outdir C:\Users\<USER>\AppData\Local\Temp\soffice_convert_xxx C:\path\to\render-test.docx
EXIT: 1
STDERR:
libpng error: Write Error
--- DOCX->ODT ---
CMD: soffice -env:UserInstallation=file://C:\Users\<USER>\AppData\Local\Temp\soffice_profile_xxx --invisible --headless --norestore --convert-to odt --outdir C:\Users\<USER>\AppData\Local\Temp\soffice_convert_xxx C:\path\to\render-test.docx
EXIT: 1
STDERR:
libpng error: Write Error
The same error persisted after:
- MSI repair via
msiexec.exe /fa {product-code} winget install --id TheDocumentFoundation.LibreOffice -e --silent --force
Expected behavior
render_docx.py should successfully render a DOCX to page PNGs on Windows when LibreOffice and Poppler are installed, or should generate a Windows-compatible LibreOffice UserInstallation URI.
Reproduction outline
- On Windows, create a simple DOCX, e.g. a heading and one paragraph.
- Run the Documents plugin renderer:
python render_docx.py C:\path\to\render-test.docx --output_dir C:\path\to\rendered --emit_pdf
- Observe the failure above.
Additional evidence
Direct LibreOffice conversion works for the same file:
& 'C:\Program Files\LibreOffice\program\soffice.exe' --headless --convert-to pdf --outdir C:\path\to\out C:\path\to\render-test.docx
The PDF is produced successfully.
Poppler PDF-to-PNG conversion also works:
pdftoppm -png C:\path\to\render-test.pdf C:\path\to\page
The PNG is produced successfully.
This provides a viable workaround:
DOCX -> LibreOffice direct PDF export -> pdftoppm PNG rasterization
Suspected cause
render_docx.py appears to build the LibreOffice profile argument by concatenating:
-env:UserInstallation=file:// + user_profile
On Windows this produces a value like:
file://C:\Users\<USER>\AppData\Local\Temp\soffice_profile_xxx
That may not be a valid/stable file URI for LibreOffice on Windows. A more Windows-compatible URI would likely be closer to:
file:///C:/Users/<USER>/AppData/Local/Temp/soffice_profile_xxx
Using pathlib.Path(user_profile).as_uri() or equivalent URI normalization may be safer.
Notes
No customer documents were used for this report. The failure was reproduced with a trivial test DOCX.
5 Comments
Proposed Solution: LibreOffice Windows Temp Profile Cleanup Race Condition
Root Cause
On Windows,
tempfile.TemporaryDirectory()cleanup fires before LibreOffice releases file handles to theUserInstallationprofile directory. Windows file locking semantics keep handles open until the process fully terminates, causingPermissionError: [WinError 32].Fix
Replace
TemporaryDirectorycontext manager withmkdtemp()+ Windows-aware cleanup with retry:Impact
Testing
Full solution:
solutions/codex-24210-libreoffice-windows-temp-profile-fix.mdThanks for taking a look and proposing this. I agree that replacing
TemporaryDirectory()withmkdtemp()plus Windows-aware retry cleanup is a good defensive fix for LibreOffice on Windows, especially forPermissionError: [WinError 32]cases wheresoffice.binstill holds profile files open.One additional data point from my reproduction: the failure I am seeing appears to happen before temp directory cleanup. The renderer fails during the DOCX -> PDF / DOCX -> ODT conversion step itself:
At that point no intermediate PDF is produced, so this does not look like only a post-conversion cleanup race in my environment.
I think the cleanup retry would still be useful, but the Windows
UserInstallationargument may also need attention. The current command shown by the renderer looks like:I suspect LibreOffice on Windows may be more reliable with a normalized file URI such as:
or whatever
pathlib.Path(profile).as_uri()produces.For comparison, direct conversion without the temporary profile works on the same machine/file:
and then
pdftoppm -pngworks on the resulting PDF.So my current guess is that a robust Windows fix may need both:
UserInstallation, andHappy to test again if a patch or updated plugin build becomes available.
Confirmed on a newer Documents bundle as well (
openai-primary-runtime/documents/26.601.10930), and I was able to turn this into a working local fix.There appear to be two Windows-specific bugs in
render_docx.py, not just one:UserInstallationURI is malformed on WindowsThe current renderer builds:
That is not a valid Windows file URI for LibreOffice. Changing it to a real file URI fixes the profile argument:
which produces:
sofficeshould not be used for headless scripted runsIn this environment,
subprocess.run(["soffice", ...])ended up launchingsoffice.exe(GUI launcher), not the console-safesoffice.com, which is consistent with the misleading popup/hang behavior people are seeing.Process inspection showed commands like:
and the actual Windows process tree contained
soffice.exe/soffice.binrather than an explicitsoffice.cominvocation.For the renderer, explicitly resolving
soffice.comon Windows avoids the GUI path:and then use that resolved executable in the conversion commands instead of bare
"soffice".Result after patch
With both changes applied locally:
render_docx.pycompleted with exit code0page-1.pngwas written successfully from a trivial test DOCX using the bundled Codex Python/runtimeSo from this reproduction, the robust Windows fix looks like:
UserInstallationwithPath(...).as_uri()soffice.comfor Windows headless/scripted executionI think (1) and (2) are the changes that actually unblock rendering in the failing path.
Additional confirmation from another Windows/Codex Desktop reproduction.
The user-visible symptom can be a LibreOffice GUI dialog rather than only a renderer traceback:
In this reproduction,
bootstrap.iniwas not actually corrupted. LibreOffice itself was usable (soffice --versionworked and direct DOCX -> PDF conversion worked). The failing renderer command was the same malformed WindowsUserInstallationURI shape already discussed here:After changing the renderer to build the argument with:
LibreOffice received:
and the
bootstrap.inidialog no longer appeared. The Documents renderer was then able to produce the intermediate PDF from a trivial DOCX using bundleopenai-primary-runtime/documents/26.601.10930and LibreOffice26.2.3.2.One related follow-up: on this Windows environment, once the LibreOffice URI issue was fixed, the next failure was PDF rasterization because Poppler was not available in PATH:
Installing
pypdfium2into the bundled Codex Python runtime and using it as a fallback allowed the renderer to complete with exit code0and writepage-1.png. That is separate from the LibreOffice URI bug, but it means a robust Windows Documents fix may also need either bundled Poppler or a non-Poppler PDF rasterization fallback.Additional confirmation from
openai-primary-runtime/documents/26.630.12135on Windows.I hit the same renderer failure path and was able to get a local hotfix working. There were two separate Windows issues in this bundle:
UserInstallationargument is still built as a malformed Windows file URL:In my repro this caused
soffice.binto hang and produce no intermediate PDF. Replacing that with a real file URI fixed the DOCX -> PDF step:In this environment Poppler was actually bundled, but the renderer did not locate the real binaries. The available command wrappers were under:
while the real executables were under:
Passing that real Poppler directory to
pdf2imagefixed the PDF -> PNG step:After applying both local changes, I verified:
page-1.pngEnvironment:
So for the current bundle, the robust Windows fix likely needs both:
UserInstallationwithPath(...).resolve().as_uri()dependencies/native/poppler/Library/bin) topdf2image, rather than relying on PATH /.cmdwrappers