From 33aad5ac655ca3f11a31bd42d62913504acf25d1 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 --- 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 b93f480..23ad892 100644 --- a/src/server/routes/config.jl +++ b/src/server/routes/config.jl @@ -269,7 +269,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 52040e2..fd35b26 100644 --- a/src/server/routes/databases.jl +++ b/src/server/routes/databases.jl @@ -28,7 +28,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