Fix compile error propagation: 'failure' instead of HTTP 500
Build and Deploy Verso / deploy (push) Successful in 10m37s

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 <noreply@anthropic.com>
This commit is contained in:
claude
2026-05-31 13:22:58 +00:00
parent 0323fd4813
commit 09f5329a07
2 changed files with 24 additions and 11 deletions
+2 -1
View File
@@ -89,9 +89,10 @@ export default CommandRunner = {
err.terminated = true err.terminated = true
return callback(err) return callback(err)
} else if (code === 1) { } 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 = new Error('exited')
err.code = code err.code = code
err.stdout = stdout // preserve captured output for callers
return callback(err) return callback(err)
} else { } else {
return callback(null, { stdout, exitCode: code }) return callback(null, { stdout, exitCode: code })
+22 -10
View File
@@ -11,7 +11,10 @@ function runQuarto(compileName, options, callback) {
const { directory, mainFile, image, environment, compileGroup } = options const { directory, mainFile, image, environment, compileGroup } = options
const timeout = options.timeout || 60000 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) const command = _buildQuartoCommand(mainFile)
@@ -26,22 +29,31 @@ function runQuarto(compileName, options, callback) {
null, null,
function (error, output) { function (error, output) {
delete ProcessTable[compileName] delete ProcessTable[compileName]
if (error) {
// Still try to write whatever output we captured before the error // Propagate real process-level errors (killed, timed out) but NOT
_writeLogOutput(compileName, directory, output, () => callback(error)) // ordinary non-zero exit codes from Quarto itself. A Quarto compile
return // 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) { 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 // Quarto writes all progress and error messages to stderr; without this
// merge the UI would show nothing in the log panel on failure. // the log panel would be empty on failure.
// LocalCommandRunner replaces $COMPILE_DIR before the shell sees the string, // LocalCommandRunner replaces $COMPILE_DIR before the shell sees the
// so the substitution is safe and no shell variable expansion occurs. // string, so no shell variable expansion occurs for that token.
const quartoArgs = [ const quartoArgs = [
'quarto', 'quarto',
'render', 'render',