From 09f5329a07f502c41a5eadbb3359187eeab74fa0 Mon Sep 17 00:00:00 2001 From: claude Date: Sun, 31 May 2026 13:22:58 +0000 Subject: [PATCH] Fix compile error propagation: 'failure' instead of HTTP 500 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit LocalCommandRunner: attach captured stdout to the error object when exit code is 1, so callers can read Quarto's output even on failure. QuartoRunner: stop propagating plain 'exited' errors from Quarto up to CompileManager. A Quarto exit-code-1 is a compile failure, not a server error — CLSI already detects failure by the absence of output.pdf and returns status='failure' (HTTP 200). Previously it fell through to the generic error handler (HTTP 500), which caused the frontend to show "Server Error" instead of the log panel. Only true process-level errors (terminated, timedout) are propagated. Co-Authored-By: Claude Sonnet 4.6 --- services/clsi/app/js/LocalCommandRunner.js | 3 +- services/clsi/app/js/QuartoRunner.js | 32 +++++++++++++++------- 2 files changed, 24 insertions(+), 11 deletions(-) diff --git a/services/clsi/app/js/LocalCommandRunner.js b/services/clsi/app/js/LocalCommandRunner.js index e3f2463e60..698881beba 100644 --- a/services/clsi/app/js/LocalCommandRunner.js +++ b/services/clsi/app/js/LocalCommandRunner.js @@ -89,9 +89,10 @@ export default CommandRunner = { err.terminated = true return callback(err) } else if (code === 1) { - // exit status from chktex + // exit status from chktex (and any compiler that exits 1 on failure) err = new Error('exited') err.code = code + err.stdout = stdout // preserve captured output for callers return callback(err) } else { return callback(null, { stdout, exitCode: code }) diff --git a/services/clsi/app/js/QuartoRunner.js b/services/clsi/app/js/QuartoRunner.js index 5a3e89dc33..c664bc5691 100644 --- a/services/clsi/app/js/QuartoRunner.js +++ b/services/clsi/app/js/QuartoRunner.js @@ -11,7 +11,10 @@ function runQuarto(compileName, options, callback) { const { directory, mainFile, image, environment, compileGroup } = options const timeout = options.timeout || 60000 - logger.debug({ directory, timeout, mainFile, compileGroup }, 'starting quarto compile') + logger.debug( + { directory, timeout, mainFile, compileGroup }, + 'starting quarto compile' + ) const command = _buildQuartoCommand(mainFile) @@ -26,22 +29,31 @@ function runQuarto(compileName, options, callback) { null, function (error, output) { delete ProcessTable[compileName] - if (error) { - // Still try to write whatever output we captured before the error - _writeLogOutput(compileName, directory, output, () => callback(error)) - return + + // Propagate real process-level errors (killed, timed out) but NOT + // ordinary non-zero exit codes from Quarto itself. A Quarto compile + // failure (exit code 1) is not a server error — the absence of + // output.pdf is sufficient for CompileController to return 'failure'. + if (error && (error.terminated || error.timedout)) { + return callback(error) } - _writeLogOutput(compileName, directory, output, () => callback(null, output)) + + // On exit-code-1 errors LocalCommandRunner attaches stdout to the + // error object; merge it so _writeLogOutput can persist it. + const combined = output || (error ? { stdout: error.stdout || '' } : null) + _writeLogOutput(compileName, directory, combined, () => + callback(null, combined) + ) } ) } function _buildQuartoCommand(mainFile) { - // Run through a POSIX shell so we can merge stderr into stdout. + // Run through a POSIX shell so stderr is merged into stdout (2>&1). // Quarto writes all progress and error messages to stderr; without this - // merge the UI would show nothing in the log panel on failure. - // LocalCommandRunner replaces $COMPILE_DIR before the shell sees the string, - // so the substitution is safe and no shell variable expansion occurs. + // the log panel would be empty on failure. + // LocalCommandRunner replaces $COMPILE_DIR before the shell sees the + // string, so no shell variable expansion occurs for that token. const quartoArgs = [ 'quarto', 'render',