LibreChat/api
Danny Avila ee0a51ac19
🪵 fix: Log Code API Auth Header Failures With Request Context (#15592)
* 🪵 fix: Log Code API Auth Header Failures With Request Context

Every Code API tool LibreChat builds — `execute_code`, `bash_tool` and
the PTC tools — resolves its auth headers through
`codeExecutionAuthHeaders`. When that throws, `@librechat/agents`
replaces it with a fixed "Code execution is not authorized" string
before either the model or the operator sees it, and the SDK's own
console diagnostic carries no request or user id.

Log the failure here before rethrowing. Winston attaches requestId,
userId and tenantId to every record, so this is the only line that can
be correlated with the `[code-env:inject]` events surrounding a failed
execution. Rethrowing unchanged keeps the SDK's sanitization intact —
the model-facing message does not change.

* 🧵 fix: Carry Non-Error Auth Failures Into the Log Message

winston folds an `Error` meta's message and stack into `info.message`
before any format runs, so an Error cause already survives the plain
console format that prints `info.message` alone. A non-Error rejection
does not: winston drops a string or plain-object meta from that format
entirely, leaving the same generic line the SDK's sanitization produces.

Append the cause to the message only when it is not an Error, so the
Error path stays free of a duplicated message. A new spec renders both
paths through a real winston transport whose format prints nothing but
`info.message`, which is what pins the distinction.

* 🧯 fix: Keep the Auth Failure Cause Out of Winston Metadata

Passing the caught value as positional metadata had three separate
failure modes, all measured against real winston.

Winston merges object metadata onto the log record, so a rejection
carrying `tenantId`, `userId` or `event_name` lands on top of the fields
`attachRequestContext` fills in — and that function deliberately keeps an
already-present value, so the failure would be attributed to the wrong
user or have its identity stripped by a matching event name. An `Error`
is no safer: only its custom enumerable properties are copied, which is
exactly the reachable set.

Any metadata also arms `format.splat()`, which then treats a `%s` in the
cause as a substitution token: rejecting with `service said %s
unavailable` rendered `service said service said %s unavailable
unavailable`.

And `String()` throws on a null-prototype object, so an exotic rejection
made the catch block throw its own formatting error instead of logging
and rethrowing the original.

The cause is now derived through a guarded formatter and inlined in the
message, with no metadata at all — which is also what makes it visible on
the console format that prints `info.message` alone. The rendering spec
now runs through `errors()` and `splat()` as well as the bare printf, and
restores each transport's previous `silent` value rather than assuming it
was false.

* 🔑 fix: Keep Signing Material Out of Code API Key Load Failures

Node 24's JSON `SyntaxError` quotes an excerpt of the source around the
offending token, so a malformed `CODEAPI_JWT_PRIVATE_JWK_JSON` produces
`Unexpected token 'M', ..."d":MIIEvgIBAD"... is not valid JSON` — private
key material inside an error message, which every caller then logs. The
message redaction patterns do not recognize a bare base64 fragment, so
nothing downstream would have caught it.

`createSigningKey` now converts any load failure into a diagnostic naming
the failure and the key format it attempted, and nothing more. That makes
the message safe by construction rather than by scrubbing, which matters
because the log added in this branch is what would have persisted it.

Also guard the `name`/`message` reads in the cause formatter: a proxy can
throw from either accessor, which would have replaced the rejection with
a formatting error and logged nothing at all.

* chore: bump agents sdk
2026-09-04 10:38:49 -04:00
..
app 🫙 fix: Drop Blank Content Blocks From Promptless Sends (#15530) 2026-09-02 20:15:00 -04:00
cache ⚡ perf: Avoid Parallel Full and Paged Prompt Loading on Startup (#15031) 2026-08-25 21:21:17 -04:00
config
db 🐇 refactor: Decouple Meilisearch From Document Saves (#15109) 2026-08-21 22:26:44 -04:00
models
server 🕯️ feat: Search Text in Attached Workspaces (#15525) 2026-09-04 10:07:06 -04:00
strategies 🔐 fix: Keep OpenID Metadata Out of Logs and Traces (#15470) 2026-09-01 13:22:12 -04:00
test 🔐 fix: Redirect OAuth Browser Navigations Instead of Rendering JSON (#15440) 2026-08-31 21:13:47 -04:00
utils ⚡ feat: Add Gemini 3.8 Flash Support (#15519) 2026-09-02 15:06:40 -04:00
jest.config.js
jsconfig.json
package.json 🪵 fix: Log Code API Auth Header Failures With Request Context (#15592) 2026-09-04 10:38:49 -04:00
typedefs.js