On Windows, io.open() returns nil, nil when opening a path returned by os.tmpname() for writing. The second return value should be an error string per the Lua 5.3 reference manual (§6.8), but it is nil. This appears to happen because openout_any = p (paranoid mode) blocks writes outside the current working directory. os.tmpname() returns an absolute path to the system temp directory, which is outside CWD. The blocking itself is reasonable, but the missing error string makes debugging difficult.

LuaHBTeX version: tested both 1.22.0 (TeX Live 2025, Windows), and 1.24.0 (TeX Live 2026, Windows)

MWE:

\documentclass{article}
\begin{document}
\directlua{
  local t = os.tmpname()
  local f, e = io.open(t, "w")
  texio.write_nl("tmpname: " .. t)
  texio.write_nl("file:   " .. tostring(f))
  texio.write_nl("error:  " .. tostring(e))
  local f2, e2 = io.open("test-cwd.tmp", "w")
  texio.write_nl("cwd file:  " .. tostring(f2))
  if f2 then f2:close(); os.remove("test-cwd.tmp") end
}
\end{document}

Output:

tmpname: C:\Users\Clemens\AppData\Local\Temp\s1gyk.0
file:   nil
error:  nil
cwd file: file (0000000007DDFAB0)

Expected: The error line should contain a descriptive string (e.g., "Permission denied") rather than nil.

Root cause in luatex-core.lua (v1.161):

The luatex_io_open override (lines 65-76) delegates to validoutput:

local function validoutput(name,how)
    return type(how) == "string" and find(how,"w") and kpse_outputnameok(name) and io_open(name,how)
end

When kpse_outputnameok(name) returns false, Lua's short-circuit "and" stops the chain and returns false -- the real io_open is never called, so no error string is ever produced. luatex_io_open then returns this bare falsy value with no second return value, violating the io.open contract (nil, errmsg, errno).

The same issue exists in luatex_io_open_readonly (lines 78-84).

Suggested fix:

local function luatex_io_open(name,how)
    local handle = validinput(name,how)
    if handle then
        kpse_recordinputfile(name,"r")
    else
        handle = validoutput(name,how)
        if handle then
            kpse_recordoutputfile(name,"w")
        end
    end
    if not handle then
        return nil, name .. ": kpse permission denied"
    end
    return handle
end


Best regards,

Clemens

_______________________________________________
dev-luatex mailing list -- [email protected]
To unsubscribe send an email to [email protected]

Reply via email to