Skip to content

🔊 fix: surface provider, model, and status in summarization error logs - #109

Merged
danny-avila merged 4 commits into
mainfrom
fix/summarization-log-visibility
Apr 19, 2026
Merged

danny-avila merged 4 commits into
mainfrom
fix/summarization-log-visibility

Conversation

@danny-avila

Copy link
Copy Markdown
Collaborator

Summary

When summarization fails, host applications (e.g. LibreChat's default console formatter) frequently strip metadata from the winston info object — the user ends up seeing only a generic Summarization LLM call failed line with no indication of which provider/model misbehaved or what the underlying error actually was.

This PR folds the critical bits (provider/model label, and HTTP status when available) directly into the log message string so they survive any downstream formatter. Structured metadata stays on the event for JSON backends.

Before

error: [agents:summarize] Summarization LLM call failed
warn:  [agents:summarize] Summarization failed, falling back to metadata stub

After

error: [agents:summarize] Summarization LLM call failed [azureOpenAI/gpt-5.4-mini] (HTTP 404): The API deployment for this resource does not exist.
warn:  [agents:summarize] Summarization failed, falling back to metadata stub [azureOpenAI/gpt-5.4-mini] (HTTP 404): ...

Changes

  • New describeProviderError helper that produces a log-string suffix ([provider/model] + optional (HTTP <n>) + error message) and a structured metadata bag (provider, model, errorName, errorStack, status).
  • Applied at all three summarization error sites: primary failure, fallback provider failure, and the final metadata-stub fallback.
  • No behavior changes for the happy path — only the error/warn messages are expanded.

Motivation

Downstream discussion: LibreChat-AI/LibreChat#12733

The symptom masks at least three distinct bugs (discussion #12614, issue #12721, and the reported discussion itself). Surfacing the provider, model, and HTTP status in the log message is the single highest-leverage improvement for diagnosing the whole class.

Test plan

  • npx jest --testPathPatterns 'src/summarization/__tests__' — all 24 existing tests pass.
  • npx tsc --noEmit — no new type errors.
  • Manual repro with a bogus Azure deployment name to confirm the expanded message surfaces in LibreChat's default console output (after the companion LibreChat PR lands).

…r logs

When summarization fails, host applications (e.g. LibreChat's default console
formatter) frequently strip metadata from the winston info object, leaving
users with only a generic "Summarization LLM call failed" message — no
indication of which provider/model misbehaved or why.

Fold the critical bits (provider/model label and HTTP status when present)
into the log message string itself so they survive any downstream formatter.
Keep the structured metadata bag intact for JSON/structured backends. Apply
the same treatment at each summarization error site (primary failure,
fallback failure, metadata-stub fallback).

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: a0afe3e6a0

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/summarization/node.ts Outdated
Comment on lines +394 to +395
const status =
errObj.status ?? errObj.statusCode ?? errObj.response?.status;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Guard nullish errors before reading status fields

describeProviderError treats err as an object and immediately reads status/statusCode/response.status; if a provider throws null/undefined (valid JS throw values), this code raises a new TypeError inside the catch path. That aborts the intended fallback behavior and can prevent metadata-stub recovery, turning a handled summarization failure into an unhandled crash. Add a non-null object guard before accessing status properties.

Useful? React with 👍 / 👎.

Comment thread src/summarization/node.ts Outdated
Comment on lines +504 to +506
fbErr,
clientConfig.provider,
clientConfig.modelName

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Report failing fallback provider in warning logs

The fallback error logger passes clientConfig.provider/modelName (the primary model) into describeProviderError, even though fbErr comes from tryFallbackProviders and may be from a different provider. In mixed-provider setups this mislabels fallback failures, which can send operators to the wrong integration while debugging incidents.

Useful? React with 👍 / 👎.

@danny-avila

Copy link
Copy Markdown
Collaborator Author

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: a0afe3e6a0

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/summarization/node.ts Outdated
Comment on lines +503 to +506
const fbDescribed = describeProviderError(
fbErr,
clientConfig.provider,
clientConfig.modelName

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Use failing fallback identity in fallback error logs

fbErr here comes from tryFallbackProviders, which throws the last fallback provider error after iterating fallbacks (src/llm/invoke.ts). Logging it with clientConfig.provider/clientConfig.modelName always attributes that failure to the primary provider, so operators can be sent to the wrong deployment when diagnosing incidents; the provider/model in this warning should reflect the fallback that actually failed (or be omitted if unavailable).

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed in 8b6d27a. Added describeFallbackError which labels the log with the list of fallback providers attempted instead of reusing the primary's provider/model.

Comment thread src/summarization/node.ts Outdated
Comment on lines +394 to +395
const status =
errObj.status ?? errObj.statusCode ?? errObj.response?.status;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Guard nullish thrown values before reading HTTP status

describeProviderError assumes the caught value is property-accessible, but catch can receive null/undefined; in that case errObj.status throws a new TypeError and masks the original provider failure. This can break the intended graceful path (including metadata-stub fallback) specifically when an upstream integration throws a nullish value, so status extraction needs a null/object guard or optional chaining on err itself.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed in 8b6d27a. Extracted an extractHttpStatus helper with an explicit err == null || typeof err !== 'object' guard. Good catch — TypeScript's no-unnecessary-condition rule had stripped the optional chaining after lint-fix, which is exactly what you flagged.

…t provider

Two issues surfaced by code review on the initial logging pass:

1. `describeProviderError` cast `err` to a non-nullable object and read
   `.status`/`.statusCode`/`.response?.status`; TypeScript's no-unnecessary-
   condition rule then stripped the optional chaining on `errObj`. If a
   provider throws `null` or `undefined` (both legal throw targets in JS),
   the status extraction would itself raise `TypeError` and mask the
   original summarization failure. Extract status through a dedicated
   helper with an explicit `err == null` guard so the intended metadata-stub
   recovery path is preserved for any thrown value.

2. The fallback-exhausted warning reused the primary provider/model label
   for the error thrown out of `tryFallbackProviders`. That error actually
   comes from the last attempted fallback and is not necessarily the
   primary integration. Introduce `describeFallbackError` which labels the
   log with the list of fallback providers attempted, so operators are
   pointed at the right deployment when diagnosing incidents.

Reviewed-by: Codex (P1 + P2 findings on PR #109).
@danny-avila

Copy link
Copy Markdown
Collaborator Author

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 8b6d27aa7e

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/summarization/node.ts Outdated
fallbacks: ReadonlyArray<{ provider: string | Providers }>
): { suffix: string; data: Record<string, unknown> } {
const errMsg = err instanceof Error ? err.message : String(err);
const providerNames = fallbacks.map((f) => String(f.provider));

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Validate fallback entries before formatting provider list

describeFallbackError assumes every element in fallbacks is a non-null object and immediately does fallbacks.map((f) => String(f.provider)). When runtime config is malformed (for example fallbacks: [undefined] or a sparse array), tryFallbackProviders can still catch per-fallback failures, but this helper throws inside the surrounding catch (fbErr) block, which prevents the code from reaching the metadata-stub fallback path. In that scenario, a recoverable summarization failure becomes an uncaught exception.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed in dc5456e. Widened describeFallbackError's parameter type to ReadonlyArray<unknown>, filter out malformed entries (null/undefined/missing provider), and fall back to a no-fallbacks label when none remain. The logging path is now total on any input.

`describeFallbackError` previously trusted that every element in the
`fallbacks` array was a non-null object with a `provider` field. If
runtime config is malformed (null/undefined entries, missing provider),
reading `.provider` from inside the surrounding `catch (fbErr)` block
would throw a fresh TypeError, promoting a recoverable summarization
failure into an uncaught exception and blocking the metadata-stub
fallback.

Widen the parameter to `ReadonlyArray<unknown>`, filter out malformed
entries, and fall back to `no-fallbacks` label when none remain. The
logging path is now total on any input.

Reviewed-by: Codex (P2 finding on PR #109, commit 8b6d27a).
@danny-avila

Copy link
Copy Markdown
Collaborator Author

@codex review

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Keep it up!

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@danny-avila

Copy link
Copy Markdown
Collaborator Author

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: dc5456ee42

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/summarization/node.ts Outdated
fallbacks: ReadonlyArray<unknown>
): { suffix: string; data: Record<string, unknown> } {
const errMsg = err instanceof Error ? err.message : String(err);
const providerNames = fallbacks

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Validate fallback list before mapping in error formatting

describeFallbackError assumes fallbacks is an array and calls .map(...), but the caller sources fallbacks from clientOptions via an unchecked cast from unknown. If runtime config provides a malformed non-array value with a truthy length (for example a string), tryFallbackProviders can fail and this catch-path formatter then throws TypeError: fallbacks.map is not a function, which escapes the error handler and prevents the metadata-stub fallback from running.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed in bff9449. Widened the parameter to unknown and coerced with Array.isArray; also coerce the call-site fallbacks binding so tryFallbackProviders never sees a non-array. The error-logging path is now total on any input shape.

`describeFallbackError` previously assumed `fallbacks` was an array because
the type signature said so. In practice the value originates from
`clientConfig.clientOptions` via an unchecked `unknown` cast, so a
malformed runtime config (e.g. a string with truthy `length`) would reach
`.map(...)` and throw `TypeError: fallbacks.map is not a function` —
escaping the surrounding `catch (fbErr)` and blocking the metadata-stub
recovery path.

Widen the parameter to `unknown`, coerce to an empty array when it isn't
one. Also coerce the call site's `fallbacks` binding with `Array.isArray`
so `tryFallbackProviders` doesn't see a non-array either.

Reviewed-by: Codex (P2 finding on PR #109, commit dc5456e).
@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Breezy!

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@danny-avila

Copy link
Copy Markdown
Collaborator Author

@codex review

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Breezy!

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@danny-avila
danny-avila merged commit d59b4cb into main Apr 19, 2026
4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant