Skip to content

fix: cancelled state in logs - #4831

Merged
Pratham-Mishra04 merged 1 commit into
devfrom
07-01-fix_cancelled_state_in_logs
Jul 2, 2026
Merged

Pratham-Mishra04 merged 1 commit into
devfrom
07-01-fix_cancelled_state_in_logs

Conversation

@TejasGhatte

@TejasGhatte TejasGhatte commented Jul 1, 2026

Copy link
Copy Markdown
Collaborator

Summary

Requests cancelled by the client (e.g. context deadline exceeded, HTTP 499) were being logged with status="error", making it impossible to distinguish genuine provider/network failures from client-initiated cancellations. This PR introduces a dedicated "cancelled" log status for those cases.

Changes

  • Introduced log status constants (logStatusProcessing, logStatusSuccess, logStatusError, logStatusCancelled) to replace raw string literals throughout main.go and operations.go.
  • Added logStatusForError(*schemas.BifrostError) string which returns "cancelled" when the error represents a client cancellation (HTTP 499, RequestCancelled error type, or a RequestTimedOut whose message indicates the timeout was driven by a context deadline), and "error" otherwise.
  • Provider-side timeouts (where the message matches ErrProviderRequestTimedOut) continue to produce status="error" — only context-driven timeouts are treated as cancellations.
  • Updated the existing cancelled-stream test assertion from "error" to "cancelled".
  • Added two new tests: TestPostLLMHookContextTimeoutLogsCancelledStatus (context deadline → "cancelled") and TestPostLLMHookProviderTimeoutRemainsErrorStatus (provider timeout → "error").

Type of change

  • Bug fix
  • Feature
  • Refactor
  • Documentation
  • Chore/CI

Affected areas

  • Core (Go)
  • Transports (HTTP)
  • Providers/Integrations
  • Plugins
  • UI (React)
  • Docs

How to test

go test ./plugins/logging/...

Expected: all tests pass, including the two new timeout-status tests and the updated cancelled-stream test.

Breaking changes

  • Yes
  • No

Any downstream consumers filtering log rows by status="error" to catch cancelled requests will need to also handle status="cancelled". Existing "error" rows for true provider/network failures are unaffected.

Related issues

Closes #3357

Security considerations

None — this change only affects how log status strings are assigned; no auth, secrets, or PII handling is modified.

Checklist

  • I read docs/contributing/README.md and followed the guidelines
  • I added/updated tests where appropriate
  • I updated documentation where needed
  • I verified builds succeed (Go and UI)
  • I verified the CI pipeline passes locally if applicable

@coderabbitai

coderabbitai Bot commented Jul 1, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Warning

Review limit reached

You’ve reached a temporary PR review limit under our Fair Usage Limits Policy.

Your recent review volume is higher than typical usage, so adaptive limits are currently applied.

Next review available in: 45 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 86667016-1a5d-4eef-ba0c-059b043c4bd9

📥 Commits

Reviewing files that changed from the base of the PR and between f9dae1e and 8c9ad5c.

📒 Files selected for processing (3)
  • plugins/logging/main.go
  • plugins/logging/operations.go
  • plugins/logging/operations_test.go
📝 Walkthrough

Walkthrough

The logging plugin adds shared status constants and error-classification helpers, replaces hard-coded status literals across hook handling and streaming output processing, and updates tests for cancelled, timeout, success, and error status behavior.

Changes

Logging status refactor

Layer / File(s) Summary
Status constants and error-to-status mapping
plugins/logging/operations.go
Adds internal log status constants and helper functions that classify BifrostError values, then uses the shared processing status during initial log entry creation and streaming output handling.
Wire status helpers into PreLLMHook and PostLLMHook
plugins/logging/main.go
Replaces literal status strings with shared helpers across pending creation, error handling, success paths, streaming post-processing, and passthrough error flips.
Test updates for cancelled vs error status
plugins/logging/operations_test.go
Updates the cancelled-stream test and adds coverage for context-timeout cancellation, provider-timeout error handling, generic deadline-message classification, and preserved cancelled status during streaming output application.

Estimated code review effort: 2 (Simple) | ~12 minutes

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Linked Issues check ⚠️ Warning The PR does not satisfy #3357 because it only changes logging status classification and does not update billing/usage accounting for failed requests. Implement the governance billing changes so cancelled, timed-out, and failed requests still record usage, and add tests proving #3357 is addressed.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title is concise and accurately reflects the main change: adding a cancelled log state.
Description check ✅ Passed The PR description closely matches the template and includes summary, changes, testing, related issue, breaking changes, security, and checklist.
Out of Scope Changes check ✅ Passed All code changes stay within the logging plugin and its tests, with no clear unrelated or extraneous additions.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch 07-01-fix_cancelled_state_in_logs

Comment @coderabbitai help to get the list of available commands.

@CLAassistant

CLAassistant commented Jul 1, 2026

Copy link
Copy Markdown

CLA assistant check
Thank you for your submission! We really appreciate it. Like many open source projects, we ask that you sign our Contributor License Agreement before we can accept your contribution.
You have signed the CLA already but the status is still pending? Let us recheck it.

TejasGhatte commented Jul 1, 2026

Copy link
Copy Markdown
Collaborator Author

@TejasGhatte
TejasGhatte marked this pull request as ready for review July 1, 2026 18:09
@greptile-apps

greptile-apps Bot commented Jul 1, 2026

Copy link
Copy Markdown
Contributor

Confidence Score: 5/5

Safe to merge; the change is localized to the logging plugin's status classification and does not affect request routing, provider calls, or data persistence correctness.

The core logic is correct: isCancelledLogError correctly distinguishes client cancellations from provider timeouts, the streaming applyStreamingOutputToEntry fix properly populates ErrorDetailsParsed so the downstream re-derivation in PostLLMHook works end-to-end, and the serialized string ErrorDetails is still written immediately to guard against pooled-object lifetimes. The "by context" substring clause is slightly over-broad relative to "context deadline exceeded" alone, but it only applies to errors already typed as RequestTimedOut with a non-standard message, keeping the blast radius small. Tests cover the new paths well.

The isContextTimeoutLogError predicate in plugins/logging/operations.go — specifically whether the "by context" clause adds meaningful coverage beyond "context deadline exceeded" or creates unintended matches.

Important Files Changed

Filename Overview
plugins/logging/operations.go Introduces log-status constants and logStatusForError/isCancelledLogError/isContextTimeoutLogError helpers; also fixes the applyStreamingOutputToEntry path by now setting entry.ErrorDetailsParsed so the downstream re-derivation in PostLLMHook has the parsed pointer to classify status correctly.
plugins/logging/main.go All raw string literals replaced with constants; error-status assignments updated to logStatusForError(bifrostErr); passthrough-error override preserved as logStatusError; the isFinalChunk re-derivation at line 1152 now correctly calls logStatusForError(entry.ErrorDetailsParsed) which works because applyStreamingOutputToEntry now populates ErrorDetailsParsed.
plugins/logging/operations_test.go Updated cancelled-stream test expectation from "error" to "cancelled"; added four new tests covering context-timeout classification, provider-timeout classification, generic-deadline non-classification, and accumulator streaming status preservation end-to-end.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[PostLLMHook receives BifrostError] --> B{isCancelledLogError?}
    B -->|StatusCode == 499| C[logStatusCancelled]
    B -->|Type == RequestCancelled| C
    B -->|Type == RequestTimedOut| D{isContextTimeoutLogError?}
    D -->|message == ErrProviderRequestTimedOut| E[logStatusError]
    D -->|contains 'by context'| C
    D -->|contains 'context deadline exceeded'| C
    D -->|other message| E
    B -->|other type| E
    C --> F[entry.Status = 'cancelled']
    E --> G[entry.Status = 'error']

    subgraph StreamingPath [Streaming Path - isFinalChunk]
        H[applyStreamingOutputToEntry] --> I{ErrorDetails != nil?}
        I -->|yes| J[entry.ErrorDetailsParsed = err\nentry.Status = logStatusForError err\nentry.ErrorDetails = serialized]
        I -->|no| K[entry.Status = success]
        J --> L[PostLLMHook re-derives status\nlogStatusForError entry.ErrorDetailsParsed]
    end
Loading
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
flowchart TD
    A[PostLLMHook receives BifrostError] --> B{isCancelledLogError?}
    B -->|StatusCode == 499| C[logStatusCancelled]
    B -->|Type == RequestCancelled| C
    B -->|Type == RequestTimedOut| D{isContextTimeoutLogError?}
    D -->|message == ErrProviderRequestTimedOut| E[logStatusError]
    D -->|contains 'by context'| C
    D -->|contains 'context deadline exceeded'| C
    D -->|other message| E
    B -->|other type| E
    C --> F[entry.Status = 'cancelled']
    E --> G[entry.Status = 'error']

    subgraph StreamingPath [Streaming Path - isFinalChunk]
        H[applyStreamingOutputToEntry] --> I{ErrorDetails != nil?}
        I -->|yes| J[entry.ErrorDetailsParsed = err\nentry.Status = logStatusForError err\nentry.ErrorDetails = serialized]
        I -->|no| K[entry.Status = success]
        J --> L[PostLLMHook re-derives status\nlogStatusForError entry.ErrorDetailsParsed]
    end
Loading

Reviews (5): Last reviewed commit: "fix: cancelled state in logs" | Re-trigger Greptile

Comment thread plugins/logging/operations.go
Comment thread plugins/logging/operations.go

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 1

♻️ Duplicate comments (1)
plugins/logging/main.go (1)

1152-1154: 🎯 Functional Correctness | 🔴 Critical | ⚡ Quick win

Overwrites correctly-derived status due to unpopulated entry.ErrorDetailsParsed (see root cause in operations.go).

This unconditional re-derivation only produces a correct result when the preceding branch explicitly set entry.ErrorDetailsParsed (true for the bifrostErr != nil sub-branch above, where it's idempotent). For the isFinalChunk sub-branch, applyStreamingOutputToEntry (in plugins/logging/operations.go) never sets entry.ErrorDetailsParsed, so this line always evaluates logStatusForError(nil)"error", silently overwriting the cancelled status that was just computed inside applyStreamingOutputToEntry. See the companion comment on operations.go for the proposed fix (propagate entry.ErrorDetailsParsed = streamResponse.Data.ErrorDetails).

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@plugins/logging/main.go` around lines 1152 - 1154, The status is being
recomputed unconditionally from entry.ErrorDetailsParsed, which overwrites the
correctly derived cancelled state in the streaming path. Update the logic around
logStatusForError in main.go so it only re-derives status when
ErrorDetailsParsed is actually populated, or propagate the parsed error details
from applyStreamingOutputToEntry in operations.go before this check runs. Use
entry.ErrorDetailsParsed, logStatusForError, and applyStreamingOutputToEntry to
locate the fix.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@plugins/logging/operations.go`:
- Around line 387-397: The streaming error handling in
applyStreamingOutputToEntry is only populating entry.Status and the marshalled
entry.ErrorDetails, but not entry.ErrorDetailsParsed, which later causes main.go
to reclassify the status incorrectly. Update applyStreamingOutputToEntry to
assign ErrorDetailsParsed alongside ErrorDetails when
streamResponse.Data.ErrorDetails is present, matching the existing bifrostErr
handling in PostLLMHook and the other streaming call sites. Ensure the
downstream status re-derivation in plugins/logging/main.go sees a non-nil parsed
error so cancelled/timeout statuses are preserved.

---

Duplicate comments:
In `@plugins/logging/main.go`:
- Around line 1152-1154: The status is being recomputed unconditionally from
entry.ErrorDetailsParsed, which overwrites the correctly derived cancelled state
in the streaming path. Update the logic around logStatusForError in main.go so
it only re-derives status when ErrorDetailsParsed is actually populated, or
propagate the parsed error details from applyStreamingOutputToEntry in
operations.go before this check runs. Use entry.ErrorDetailsParsed,
logStatusForError, and applyStreamingOutputToEntry to locate the fix.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 39434ca4-15aa-48f7-b035-75e28e89ca1a

📥 Commits

Reviewing files that changed from the base of the PR and between a52d180 and d1e6312.

📒 Files selected for processing (3)
  • plugins/logging/main.go
  • plugins/logging/operations.go
  • plugins/logging/operations_test.go

Comment thread plugins/logging/operations.go
@TejasGhatte
TejasGhatte force-pushed the 07-01-fix_cancelled_state_in_logs branch from d1e6312 to bb364c1 Compare July 1, 2026 18:37
coderabbitai[bot]
coderabbitai Bot previously approved these changes Jul 1, 2026
@TejasGhatte
TejasGhatte changed the base branch from dev to graphite-base/4831 July 2, 2026 04:02
@TejasGhatte
TejasGhatte force-pushed the 07-01-fix_cancelled_state_in_logs branch from bb364c1 to ee2940f Compare July 2, 2026 04:02
@TejasGhatte
TejasGhatte changed the base branch from graphite-base/4831 to 07-01-fix_perplexity_responses_api_compatibility July 2, 2026 04:02

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick comments (2)
plugins/logging/operations_test.go (2)

300-303: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Prefer schemas.Ptr() over &localVar for these new status-code pointers.

New tests introduce statusCode := 504/499 locals purely to take their address (e.g., StatusCode: &statusCode). Based on learnings, the repo convention prefers bifrost.Ptr()/schemas.Ptr() over the address operator, even in test code — this file already uses schemas.Ptr(...) for the Type field a few lines below each occurrence.

♻️ Example diff for one occurrence
-	statusCode := 504
 	bifrostErr := &schemas.BifrostError{
 		IsBifrostError: true,
-		StatusCode:     &statusCode,
+		StatusCode:     schemas.Ptr(504),

Also applies to: 353-356, 385-388, 403-408

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@plugins/logging/operations_test.go` around lines 300 - 303, The new
status-code pointers in the test cases are using local variables with address-of
syntax, which is inconsistent with the repo convention. Update the BifrostError
constructions in operations_test.go to use schemas.Ptr for StatusCode, matching
how Type is already set in the same test helpers, and remove the temporary
statusCode locals from each affected test block.

Source: Learnings


400-439: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Second assertion re-implements production logic instead of exercising it.

Lines 429-434 manually duplicate the "Path B" re-derivation condition that PostLLMHook uses downstream (per the AI summary/comment on lines 429-431), rather than invoking the actual production code path. If that downstream reclassification logic in main.go changes (e.g., different condition, additional side effect), this test will keep passing even though the real behavior regressed, since it only re-runs logStatusForError inline against a hand-copied condition.

Consider either:

  • Extracting the "Path B" reclassification condition into a small helper function in operations.go that both PostLLMHook and this test call, or
  • Restructuring the test to exercise PostLLMHook end-to-end (similar to the other new tests in this file) so the actual downstream code path is verified.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@plugins/logging/operations_test.go` around lines 400 - 439, The test in
TestApplyStreamingOutputToEntryPreservesAccumulatorCancelledStatus duplicates
the downstream “Path B” reclassification logic instead of verifying the real
production path. Refactor the re-derivation condition used by PostLLMHook into a
shared helper in LoggerPlugin/operations.go and call that helper from both
PostLLMHook and this test, or switch the test to exercise PostLLMHook end-to-end
so the actual cancellation reclassification behavior is covered.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@plugins/logging/operations_test.go`:
- Around line 300-303: The new status-code pointers in the test cases are using
local variables with address-of syntax, which is inconsistent with the repo
convention. Update the BifrostError constructions in operations_test.go to use
schemas.Ptr for StatusCode, matching how Type is already set in the same test
helpers, and remove the temporary statusCode locals from each affected test
block.
- Around line 400-439: The test in
TestApplyStreamingOutputToEntryPreservesAccumulatorCancelledStatus duplicates
the downstream “Path B” reclassification logic instead of verifying the real
production path. Refactor the re-derivation condition used by PostLLMHook into a
shared helper in LoggerPlugin/operations.go and call that helper from both
PostLLMHook and this test, or switch the test to exercise PostLLMHook end-to-end
so the actual cancellation reclassification behavior is covered.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 62aa0234-7eee-4440-adda-ec49a6591c19

📥 Commits

Reviewing files that changed from the base of the PR and between bb364c1 and ee2940f.

📒 Files selected for processing (3)
  • plugins/logging/main.go
  • plugins/logging/operations.go
  • plugins/logging/operations_test.go
🚧 Files skipped from review as they are similar to previous changes (2)
  • plugins/logging/operations.go
  • plugins/logging/main.go

@TejasGhatte
TejasGhatte force-pushed the 07-01-fix_cancelled_state_in_logs branch from ee2940f to f9dae1e Compare July 2, 2026 07:32
@TejasGhatte
TejasGhatte force-pushed the 07-01-fix_perplexity_responses_api_compatibility branch from 1d6c19c to ad975da Compare July 2, 2026 07:32

Pratham-Mishra04 commented Jul 2, 2026

Copy link
Copy Markdown
Collaborator

Merge activity

  • Jul 2, 1:44 PM UTC: A user started a stack merge that includes this pull request via Graphite.
  • Jul 2, 1:50 PM UTC: Graphite rebased this pull request as part of a merge.
  • Jul 2, 1:51 PM UTC: @Pratham-Mishra04 merged this pull request with Graphite.

@Pratham-Mishra04
Pratham-Mishra04 changed the base branch from 07-01-fix_perplexity_responses_api_compatibility to graphite-base/4831 July 2, 2026 13:45
@Pratham-Mishra04
Pratham-Mishra04 changed the base branch from graphite-base/4831 to dev July 2, 2026 13:49
@Pratham-Mishra04
Pratham-Mishra04 dismissed stale reviews from coderabbitai[bot] and themself July 2, 2026 13:49

The base branch was changed.

@Pratham-Mishra04
Pratham-Mishra04 force-pushed the 07-01-fix_cancelled_state_in_logs branch from f9dae1e to 8c9ad5c Compare July 2, 2026 13:49
@Pratham-Mishra04
Pratham-Mishra04 merged commit 98b8d14 into dev Jul 2, 2026
14 of 16 checks passed
@Pratham-Mishra04
Pratham-Mishra04 deleted the 07-01-fix_cancelled_state_in_logs branch July 2, 2026 13:51
yangtuooc added a commit to yangtuooc/bifrost that referenced this pull request Jul 2, 2026
* upstream/dev:
  feat(mcp): add per-MCP-server tool execution timeout (maximhq#4472)
  fix: billing on failed responses stream requests anthropic and bedrock (maximhq#4842)
  fix: gemini openai through signature compatibility (maximhq#4810)
  fix: cancelled state in logs (maximhq#4831)
  fix: perplexity responses api compatibility (maximhq#4813)
  docs: clarify two-layer token refresh behavior and disabled-client refresh token expiry (maximhq#4849)
  fix: skip background token refresh for disabled/unconfigured MCP clients and guarantee non-nil logger in sync workers (maximhq#4848)
akhsaul pushed a commit to akhsaul/bifrost that referenced this pull request Aug 27, 2026
## Summary

Requests cancelled by the client (e.g. context deadline exceeded, HTTP 499) were being logged with `status="error"`, making it impossible to distinguish genuine provider/network failures from client-initiated cancellations. This PR introduces a dedicated `"cancelled"` log status for those cases.

## Changes

- Introduced log status constants (`logStatusProcessing`, `logStatusSuccess`, `logStatusError`, `logStatusCancelled`) to replace raw string literals throughout `main.go` and `operations.go`.
- Added `logStatusForError(*schemas.BifrostError) string` which returns `"cancelled"` when the error represents a client cancellation (HTTP 499, `RequestCancelled` error type, or a `RequestTimedOut` whose message indicates the timeout was driven by a context deadline), and `"error"` otherwise.
- Provider-side timeouts (where the message matches `ErrProviderRequestTimedOut`) continue to produce `status="error"` — only context-driven timeouts are treated as cancellations.
- Updated the existing cancelled-stream test assertion from `"error"` to `"cancelled"`.
- Added two new tests: `TestPostLLMHookContextTimeoutLogsCancelledStatus` (context deadline → `"cancelled"`) and `TestPostLLMHookProviderTimeoutRemainsErrorStatus` (provider timeout → `"error"`).

## Type of change

- [x] Bug fix
- [ ] Feature
- [ ] Refactor
- [ ] Documentation
- [ ] Chore/CI

## Affected areas

- [ ] Core (Go)
- [ ] Transports (HTTP)
- [ ] Providers/Integrations
- [x] Plugins
- [ ] UI (React)
- [ ] Docs

## How to test

```sh
go test ./plugins/logging/...
```

Expected: all tests pass, including the two new timeout-status tests and the updated cancelled-stream test.

## Breaking changes

- [x] Yes
- [ ] No

Any downstream consumers filtering log rows by `status="error"` to catch cancelled requests will need to also handle `status="cancelled"`. Existing `"error"` rows for true provider/network failures are unaffected.

## Related issues

Closes maximhq#3357

## Security considerations

None — this change only affects how log status strings are assigned; no auth, secrets, or PII handling is modified.

## Checklist

- [ ] I read `docs/contributing/README.md` and followed the guidelines
- [x] I added/updated tests where appropriate
- [ ] I updated documentation where needed
- [x] I verified builds succeed (Go and UI)
- [ ] I verified the CI pipeline passes locally if applicable
occcat pushed a commit to occcat/bifrost that referenced this pull request Sep 2, 2026
## Summary

Requests cancelled by the client (e.g. context deadline exceeded, HTTP 499) were being logged with `status="error"`, making it impossible to distinguish genuine provider/network failures from client-initiated cancellations. This PR introduces a dedicated `"cancelled"` log status for those cases.

## Changes

- Introduced log status constants (`logStatusProcessing`, `logStatusSuccess`, `logStatusError`, `logStatusCancelled`) to replace raw string literals throughout `main.go` and `operations.go`.
- Added `logStatusForError(*schemas.BifrostError) string` which returns `"cancelled"` when the error represents a client cancellation (HTTP 499, `RequestCancelled` error type, or a `RequestTimedOut` whose message indicates the timeout was driven by a context deadline), and `"error"` otherwise.
- Provider-side timeouts (where the message matches `ErrProviderRequestTimedOut`) continue to produce `status="error"` — only context-driven timeouts are treated as cancellations.
- Updated the existing cancelled-stream test assertion from `"error"` to `"cancelled"`.
- Added two new tests: `TestPostLLMHookContextTimeoutLogsCancelledStatus` (context deadline → `"cancelled"`) and `TestPostLLMHookProviderTimeoutRemainsErrorStatus` (provider timeout → `"error"`).

## Type of change

- [x] Bug fix
- [ ] Feature
- [ ] Refactor
- [ ] Documentation
- [ ] Chore/CI

## Affected areas

- [ ] Core (Go)
- [ ] Transports (HTTP)
- [ ] Providers/Integrations
- [x] Plugins
- [ ] UI (React)
- [ ] Docs

## How to test

```sh
go test ./plugins/logging/...
```

Expected: all tests pass, including the two new timeout-status tests and the updated cancelled-stream test.

## Breaking changes

- [x] Yes
- [ ] No

Any downstream consumers filtering log rows by `status="error"` to catch cancelled requests will need to also handle `status="cancelled"`. Existing `"error"` rows for true provider/network failures are unaffected.

## Related issues

Closes maximhq#3357

## Security considerations

None — this change only affects how log status strings are assigned; no auth, secrets, or PII handling is modified.

## Checklist

- [ ] I read `docs/contributing/README.md` and followed the guidelines
- [x] I added/updated tests where appropriate
- [ ] I updated documentation where needed
- [x] I verified builds succeed (Go and UI)
- [ ] I verified the CI pipeline passes locally if applicable
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.

[Bug]: Bifrost Billing Discrepancy Analysis Report

3 participants