From 7070ac346fb548a15a24b19e2ae5c1fc4af50f22 Mon Sep 17 00:00:00 2001 From: "Ankur Sinha (Ankur Sinha Gmail)" Date: Mon, 18 May 2026 20:06:16 +0100 Subject: [PATCH 1/2] Fix: Daemon stdio redirection fails on Ruby 3.4+ In Ruby 3.4+, IO#reopen no longer accepts StringIO objects as an argument. It expects a String (file path) instead. This caused tj3d and tj3webd to fail with: Fatal: no implicit conversion of StringIO into String Change .reopen(StringIO.new) back to .reopen('/dev/null', 'a') which works on all Ruby versions. Reported-by: GitHub issue #303 Assisted-by: opencode/qwen3.6-plus Signed-off-by: Ankur Sinha (Ankur Sinha Gmail) --- lib/taskjuggler/daemon/Daemon.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/taskjuggler/daemon/Daemon.rb b/lib/taskjuggler/daemon/Daemon.rb index d8c06246..193bae62 100644 --- a/lib/taskjuggler/daemon/Daemon.rb +++ b/lib/taskjuggler/daemon/Daemon.rb @@ -69,7 +69,7 @@ def start # We no longer have a controlling terminal, so these are useless. $stdin.reopen('/dev/null') - $stdout.reopen(StringIO.new) + $stdout.reopen('/dev/null', 'a') $stderr.reopen($stdout) info('daemon_pid', From f38c60256073e218c1daf757c689bdd1df8e1810 Mon Sep 17 00:00:00 2001 From: "Ankur Sinha (Ankur Sinha Gmail)" Date: Mon, 18 May 2026 20:14:55 +0100 Subject: [PATCH 2/2] Fix: ReportServlet StringIO buffers are read-only in web server context In Ruby 4.0+, StringIO.new('') unexpectedly creates a read-only buffer in the web server context. This caused report generation via the web interface to fail with: IOError: not opened for writing Use StringIO.new without arguments instead, which creates a writable buffer. Also add a missing return statement after the connect error to prevent further execution. Investigation: - StringIO.new('') works correctly in standalone Ruby scripts - StringIO.new('') works correctly in a forked daemon process - StringIO.new('') works correctly in a standalone WEBrick handler - The bug only manifests in the full TaskJuggler web server process, which involves multiple forks, DRb servers, and the complete TaskJuggler module stack loaded together - It is unclear why StringIO.new('') behaves differently in this context. Root cause not yet identified. Reported-by: GitHub issue #303 Assisted-by: opencode/qwen3.6-plus Signed-off-by: Ankur Sinha (Ankur Sinha Gmail) --- lib/taskjuggler/daemon/ReportServlet.rb | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/lib/taskjuggler/daemon/ReportServlet.rb b/lib/taskjuggler/daemon/ReportServlet.rb index d091bd6c..57c35d3c 100644 --- a/lib/taskjuggler/daemon/ReportServlet.rb +++ b/lib/taskjuggler/daemon/ReportServlet.rb @@ -105,9 +105,13 @@ def generateReport(projectId, reportId, attributes) # text from the report server. This buffer will contain the generated # report as HTML encoded text. They will be send via DRb, so we have to # extend them with DRbUndumped. - stdOut = StringIO.new('') + # + # Note: In Ruby 4.0+, StringIO.new('') unexpectedly creates a read-only + # buffer in the web server context. Using StringIO.new without arguments + # avoids this issue. Root cause not yet identified. + stdOut = StringIO.new stdOut.extend(DRbUndumped) - stdErr = StringIO.new('') + stdErr = StringIO.new stdErr.extend(DRbUndumped) begin @@ -119,6 +123,7 @@ def generateReport(projectId, reportId, attributes) end error('rs_io_connect_failed', "Can't connect IO: #{$!}") + return end # Ask the ReportServer to generate the reports with the provided ID.