From cfc9cada0404a9db04e79217fc5e87756029e6c3 Mon Sep 17 00:00:00 2001 From: "Jonathan D.A. Jewell" <6759885+hyperpolymath@users.noreply.github.com> Date: Mon, 21 Sep 2026 18:04:59 +0100 Subject: [PATCH] fix(server): log an unreadable config at warn, not error `_read_databases` and `_read_primers` both answer `json_error(400, ...)`: the route's own verdict that a malformed `config/databases.yml` or `config/primers.yml` is an operator-fixable input, not a server fault. Logging that at `@error` with a full backtrace reports a routine bad file as if the server had crashed, and the backtrace pointed into YAML.jl's parser rather than at the offending line, so it carried no diagnostic value either. The reason string is kept -- `reason=sprint(showerror, e)` is what actually names the YAML defect. Only the backtrace goes. Deliberately NOT changed: the three 500-class sites, which stay at `@error` with their backtraces -- src/server/routes/analysis_config.jl:144 src/server/routes/annotations.jl:318 src/server/routes/composition.jl:370 A 500 is the route saying it did not expect this, and there the backtrace is the only thing separating a data problem from a code bug. Dropping it would be muting a diagnostic rather than fixing one. `analysis_config.jl` already draws this line itself: its `ArgumentError` branch returns 400 with no logging at all, while everything else logs at `@error` and returns 500. `src/server/routes/pipeline.jl:520` is untouched -- it is the silent-degradation shape tracked in #31, not a severity question. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01X3hgXxWm6umMgZkjYyHnnm (cherry picked from commit e2147d5f4cf24c373e030d574359f89dbc7dd60f) --- src/server/routes/config.jl | 5 ++++- src/server/routes/databases.jl | 8 +++++++- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/src/server/routes/config.jl b/src/server/routes/config.jl index 4140e72b..aa8af5ab 100644 --- a/src/server/routes/config.jl +++ b/src/server/routes/config.jl @@ -274,7 +274,10 @@ function _read_primers() try PrimersLibrary.load(_primers_path()) catch e - @error "primers.yml is not readable" path=_primers_path() exception=(e, catch_backtrace()) + # @warn, not @error: the 400 on the next line is this function's own verdict + # that an unreadable config/primers.yml is an operator-fixable input, not a + # server fault. See the matching note in routes/databases.jl. + @warn "primers.yml is not readable" path=_primers_path() reason=sprint(showerror, e) json_error(400, "primers_unreadable", "config/primers.yml cannot be read as a primers document and must be repaired by hand: $(sprint(showerror, e))") end diff --git a/src/server/routes/databases.jl b/src/server/routes/databases.jl index 75ade1f7..2d22fd0e 100644 --- a/src/server/routes/databases.jl +++ b/src/server/routes/databases.jl @@ -19,7 +19,13 @@ function _read_databases() try DatabasesLibrary.load(_databases_path()) catch e - @error "databases.yml is not readable" path=_databases_path() exception=(e, catch_backtrace()) + # @warn, not @error: the 400 on the next line is this function's own verdict + # that an unreadable config/databases.yml is an operator-fixable input, not a + # server fault. Error severity with a backtrace reports a routine bad file as + # a crash. The reason is kept -- it is what names the YAML defect -- and only + # the backtrace goes, which pointed into YAML.jl's parser, never at the + # offending line. + @warn "databases.yml is not readable" path=_databases_path() reason=sprint(showerror, e) json_error(400, "databases_unreadable", "config/databases.yml cannot be read as a databases document and must be repaired by hand: $(sprint(showerror, e))") end