Skip to content

Comments

feat(error-rules): record matched rule details in decision chain (#416)#418

Merged
ding113 merged 2 commits intodevfrom
fix/issue-416-error-rule-log
Dec 23, 2025
Merged

feat(error-rules): record matched rule details in decision chain (#416)#418
ding113 merged 2 commits intodevfrom
fix/issue-416-error-rule-log

Conversation

@ding113
Copy link
Owner

@ding113 ding113 commented Dec 23, 2025

Summary

Records matched error rule details (ID, pattern, category, description) in the decision chain when a non-retryable client error is triggered, enabling easier debugging and troubleshooting of error rule matches.

Problem

When an error rule matches and triggers a client_error_non_retryable response, the decision chain only recorded that a match occurred, but not which specific rule was matched. This made it difficult for administrators to:

  • Identify which error rule pattern was triggered
  • Understand why a request was marked as non-retryable
  • Debug rule configuration issues

Related Issues:

Solution

Extended the error detection and decision chain infrastructure to capture and display matched rule metadata:

  1. Extended ErrorDetectionResult with ruleId and description fields
  2. Added matchedRule field to ProviderChainItem.errorDetails containing:
    • Rule ID, pattern, match type (regex/contains/exact), category
    • Description (if defined)
    • Override flags (response/statusCode)
  3. Updated provider chain formatter to display matched rule info in timeline view
  4. Added i18n support for all 5 locales (en, ja, ru, zh-CN, zh-TW)

Changes

Core Changes

File Changes
src/lib/error-rule-detector.ts Extended pattern interfaces to store ruleId and rawPattern; return these fields in detection results
src/app/v1/_lib/proxy/forwarder.ts Capture matched rule details and include in decision chain entry
src/app/v1/_lib/proxy/errors.ts Export getErrorDetectionResultAsync() for reusing cached detection results
src/types/message.ts Added matchedRule type definition to ProviderChainItem.errorDetails

UI/Formatting Changes

File Changes
src/lib/utils/provider-chain-formatter.ts Added client_error_non_retryable handling in getProviderStatus, isActualRequest, and timeline formatting

i18n Changes

  • Added translations for clientError, clientErrorNonRetryable, matchedRule, ruleId, ruleCategory, rulePattern, ruleMatchType, ruleDescription, ruleHasOverride, clientErrorNote in all 5 locales

Testing

Manual Testing

  1. Create an error rule with a specific pattern (e.g., "test error")
  2. Send a request that triggers this error rule
  3. View the request in the logs dashboard
  4. Verify the decision chain timeline shows:
    • The matched rule ID, pattern, and category
    • The rule description (if configured)
    • Override status indicators

Checklist

  • Code follows project conventions
  • Self-review completed
  • i18n translations added for all 5 locales
  • Types properly defined

Description enhanced by Claude AI

- Extend ErrorDetectionResult with ruleId and description fields
- Add matchedRule field to ProviderChainItem.errorDetails
- Record matched error rule info in client_error_non_retryable branch
- Add client_error_non_retryable formatting in provider-chain-formatter
- Add getProviderStatus and isActualRequest handling for new reason type
- Add i18n translations for all 5 locales

Closes #416

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@gemini-code-assist
Copy link
Contributor

Summary of Changes

Hello @ding113, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request significantly improves the visibility and debuggability of error handling, particularly for client-side non-retryable errors. By capturing and exposing detailed information about the specific error rule that was matched, developers can more quickly understand the cause and context of such errors. This enhancement is integrated into the existing provider chain logging and display mechanisms, supported by new internationalization strings for a consistent user experience.

Highlights

  • Enhanced Error Rule Details: The ErrorDetectionResult now includes the ruleId and description of a matched error rule, providing more context.
  • Detailed Error Logging: Matched error rule information is now recorded within ProviderChainItem.errorDetails specifically for client_error_non_retryable cases, improving debuggability.
  • Improved Provider Chain Formatting: The provider-chain-formatter has been updated to display comprehensive details of matched error rules for non-retryable client errors directly in the timeline view.
  • Internationalization Support: New translation keys have been added across five locales (English, Japanese, Russian, Simplified Chinese, Traditional Chinese) to support the display of client error messages and matched rule details.
  • Optimized Error Detection: A new getErrorDetectionResultAsync function is introduced to leverage caching, preventing redundant error rule matching for the same error object.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

@github-actions
Copy link
Contributor

🧪 测试结果

测试类型 状态
代码质量
单元测试
集成测试
API 测试

总体结果: ✅ 所有测试通过

@github-actions github-actions bot added the size/M Medium PR (< 500 lines) label Dec 23, 2025
Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request successfully adds detailed error rule matching information to the decision chain for non-retryable client errors. This is a great enhancement for debugging and observability. The changes are well-structured, touching the error detection, forwarding logic, data types, and UI formatting. The inclusion of i18n translations is also thorough. My review includes a couple of suggestions to improve code clarity and maintainability.

Comment on lines 453 to 468
const matchedRule =
detectionResult.matched &&
detectionResult.ruleId !== undefined &&
detectionResult.pattern &&
detectionResult.matchType &&
detectionResult.category
? {
ruleId: detectionResult.ruleId,
pattern: detectionResult.pattern,
matchType: detectionResult.matchType,
category: detectionResult.category,
description: detectionResult.description,
hasOverrideResponse: detectionResult.overrideResponse !== undefined,
hasOverrideStatusCode: detectionResult.overrideStatusCode !== undefined,
}
: undefined;
Copy link
Contributor

Choose a reason for hiding this comment

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

medium

The condition to check for a valid detectionResult is a bit verbose and can be fragile if a valid pattern is an empty string (which is falsy). The implementation of detect in error-rule-detector.ts ensures that if matched is true, ruleId, pattern, matchType, and category are always defined (not undefined or null).

A more robust check would explicitly check for undefined instead of relying on truthiness, which also improves readability.

Suggested change
const matchedRule =
detectionResult.matched &&
detectionResult.ruleId !== undefined &&
detectionResult.pattern &&
detectionResult.matchType &&
detectionResult.category
? {
ruleId: detectionResult.ruleId,
pattern: detectionResult.pattern,
matchType: detectionResult.matchType,
category: detectionResult.category,
description: detectionResult.description,
hasOverrideResponse: detectionResult.overrideResponse !== undefined,
hasOverrideStatusCode: detectionResult.overrideStatusCode !== undefined,
}
: undefined;
const matchedRule =
detectionResult.matched &&
detectionResult.ruleId !== undefined &&
detectionResult.pattern !== undefined &&
detectionResult.matchType !== undefined &&
detectionResult.category !== undefined
? {
ruleId: detectionResult.ruleId,
pattern: detectionResult.pattern,
matchType: detectionResult.matchType,
category: detectionResult.category,
description: detectionResult.description,
hasOverrideResponse: detectionResult.overrideResponse !== undefined,
hasOverrideStatusCode: detectionResult.overrideStatusCode !== undefined,
}
: undefined;

Comment on lines +520 to +558
if (item.reason === "client_error_non_retryable") {
const attempt = item.attemptNumber ?? actualAttemptNumber ?? 0;
timeline += `${t("timeline.clientErrorNonRetryable", { attempt })}\n\n`;

if (item.errorDetails?.provider) {
const p = item.errorDetails.provider;
timeline += `${t("timeline.provider", { provider: p.name })}\n`;
timeline += `${t("timeline.statusCode", { code: p.statusCode })}\n`;
timeline += `${t("timeline.error", { error: p.statusText })}\n`;
} else {
timeline += `${t("timeline.provider", { provider: item.name })}\n`;
if (item.statusCode) {
timeline += `${t("timeline.statusCode", { code: item.statusCode })}\n`;
}
timeline += `${t("timeline.error", { error: item.errorMessage || t("timeline.unknown") })}\n`;
}

if (item.errorDetails?.matchedRule) {
const rule = item.errorDetails.matchedRule;
timeline += `\n${t("timeline.matchedRule")}:\n`;
timeline += `${t("timeline.ruleId", { id: rule.ruleId })}\n`;
timeline += `${t("timeline.ruleCategory", { category: rule.category })}\n`;
timeline += `${t("timeline.rulePattern", { pattern: rule.pattern })}\n`;
timeline += `${t("timeline.ruleMatchType", { matchType: rule.matchType })}\n`;
if (rule.description) {
timeline += `${t("timeline.ruleDescription", { description: rule.description })}\n`;
}
timeline += `${t("timeline.ruleHasOverride", {
response: rule.hasOverrideResponse ? "true" : "false",
statusCode: rule.hasOverrideStatusCode ? "true" : "false",
})}\n`;
}

if (item.errorDetails?.request) {
timeline += formatRequestDetails(item.errorDetails.request, t);
}

timeline += `\n${t("timeline.clientErrorNote")}`;
continue;
Copy link
Contributor

Choose a reason for hiding this comment

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

medium

To improve the readability and maintainability of the formatProviderTimeline function, consider extracting the logic for formatting client_error_non_retryable into a separate helper function. This will make the main function cleaner and easier to follow.

For example, you could create a new function:

function formatClientErrorDetails(
  item: ProviderChainItem,
  t: (key: string, values?: Record<string, string | number>) => string,
  actualAttemptNumber: number | undefined
): string {
  let details = "";
  const attempt = item.attemptNumber ?? actualAttemptNumber ?? 0;
  details += `${t("timeline.clientErrorNonRetryable", { attempt })}\n\n`;

  if (item.errorDetails?.provider) {
    const p = item.errorDetails.provider;
    details += `${t("timeline.provider", { provider: p.name })}\n`;
    details += `${t("timeline.statusCode", { code: p.statusCode })}\n`;
    details += `${t("timeline.error", { error: p.statusText })}\n`;
  } else {
    details += `${t("timeline.provider", { provider: item.name })}\n`;
    if (item.statusCode) {
      details += `${t("timeline.statusCode", { code: item.statusCode })}\n`;
    }
    details += `${t("timeline.error", { error: item.errorMessage || t("timeline.unknown") })}\n`;
  }

  if (item.errorDetails?.matchedRule) {
    const rule = item.errorDetails.matchedRule;
    details += `\n${t("timeline.matchedRule")}:\n`;
    details += `${t("timeline.ruleId", { id: rule.ruleId })}\n`;
    details += `${t("timeline.ruleCategory", { category: rule.category })}\n`;
    details += `${t("timeline.rulePattern", { pattern: rule.pattern })}\n`;
    details += `${t("timeline.ruleMatchType", { matchType: rule.matchType })}\n`;
    if (rule.description) {
      details += `${t("timeline.ruleDescription", { description: rule.description })}\n`;
    }
    details += `${t("timeline.ruleHasOverride", {
      response: rule.hasOverrideResponse ? "true" : "false",
      statusCode: rule.hasOverrideStatusCode ? "true" : "false",
    })}\n`;
  }

  if (item.errorDetails?.request) {
    details += formatRequestDetails(item.errorDetails.request, t);
  }

  details += `\n${t("timeline.clientErrorNote")}`;
  return details;
}

Then, in formatProviderTimeline, you can simply call this function:

// === 不可重试的客户端错误 ===
if (item.reason === "client_error_non_retryable") {
  timeline += formatClientErrorDetails(item, t, actualAttemptNumber);
  continue;
}

Copy link
Contributor

@github-actions github-actions bot left a comment

Choose a reason for hiding this comment

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

Code Review Summary

No significant issues identified in this PR.

PR Size: M

  • Lines changed: 204
  • Files changed: 10

Review Coverage

  • Logic and correctness - Clean
  • Security (OWASP Top 10) - Clean
  • Error handling - Clean
  • Type safety - Clean
  • Documentation accuracy - Clean
  • Test coverage - Adequate
  • Code clarity - Good

Automated review by Codex AI

Copy link
Contributor

@github-actions github-actions bot left a comment

Choose a reason for hiding this comment

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

  • Reviewed PR #418 (188 additions, 16 deletions, 10 files) → size M
  • Applied label size/M
  • Submitted a gh pr review --comment summary on PR #418; no inline review comments (no diff-scoped issues met the reporting threshold)

Copy link
Contributor

@github-actions github-actions bot left a comment

Choose a reason for hiding this comment

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

Code Review Summary

No significant issues identified in this PR. The implementation correctly extends the error detection and decision chain infrastructure to capture and display matched rule metadata for debugging purposes.

PR Size: M (Medium)

  • Lines changed: 204 (188 additions + 16 deletions)
  • Files changed: 10

Review Coverage

  • Logic and correctness - Clean
    • Properly extends ErrorDetectionResult with ruleId and description fields
    • Correctly constructs matchedRule object only when all required fields are present
    • Properly reuses the cached detection result via getErrorDetectionResultAsync
  • Security (OWASP Top 10) - Clean
    • No sensitive data exposure in logged rule information
    • Rule patterns are already stored in the database and visible to admins
  • Error handling - Clean
    • New getErrorDetectionResultAsync properly delegates to existing detectErrorRuleOnceAsync
    • Existing cache and error handling patterns are preserved
  • Type safety - Clean
    • New matchedRule type properly defined in ProviderChainItem.errorDetails
    • Interface extensions in ErrorDetectionResult and pattern interfaces are consistent
  • Documentation accuracy - Clean
    • Chinese comments appropriately explain the purpose of new fields and functions
  • Test coverage - Adequate
    • Changes are observability/logging focused; existing detection logic unchanged
  • Code clarity - Good
    • Follows existing patterns in the codebase
    • Consistent with how other decision chain details are recorded

Automated review by Claude AI

Copy link
Contributor

@github-actions github-actions bot left a comment

Choose a reason for hiding this comment

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

Code Review Summary

No significant issues identified in this PR. The implementation correctly extends the error detection and decision chain infrastructure to capture and display matched rule metadata for debugging purposes.

PR Size: M (Medium)

  • Lines changed: 204 (188 additions + 16 deletions)
  • Files changed: 10

Review Coverage

  • Logic and correctness - Clean
    • Properly extends ErrorDetectionResult with ruleId and description fields
    • Correctly constructs matchedRule object only when all required fields are present
    • Properly reuses the cached detection result via getErrorDetectionResultAsync
  • Security (OWASP Top 10) - Clean
    • No sensitive data exposure in logged rule information
    • Rule patterns are already stored in the database and visible to admins
  • Error handling - Clean
    • New getErrorDetectionResultAsync properly delegates to existing detectErrorRuleOnceAsync
    • Existing cache and error handling patterns are preserved
  • Type safety - Clean
    • New matchedRule type properly defined in ProviderChainItem.errorDetails
    • Interface extensions in ErrorDetectionResult and pattern interfaces are consistent
  • Documentation accuracy - Clean
    • Chinese comments appropriately explain the purpose of new fields and functions
  • Test coverage - Adequate
    • Changes are observability/logging focused; existing detection logic unchanged
  • Code clarity - Good
    • Follows existing patterns in the codebase
    • Consistent with how other decision chain details are recorded

Automated review by Claude AI

Address PR review feedback from Gemini Code Assist: replace truthiness
checks with explicit !== undefined comparisons for pattern, matchType,
and category fields. This prevents potential false negatives when these
fields contain empty strings.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@github-actions
Copy link
Contributor

🧪 测试结果

测试类型 状态
代码质量
单元测试
集成测试
API 测试

总体结果: ✅ 所有测试通过

@ding113 ding113 merged commit 417c0bc into dev Dec 23, 2025
8 checks passed
@github-project-automation github-project-automation bot moved this from Backlog to Done in Claude Code Hub Roadmap Dec 23, 2025
@ding113 ding113 deleted the fix/issue-416-error-rule-log branch December 23, 2025 17:45
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

1 participant