Skip to content

[duplicate-code] Duplicate Code Pattern: Duplicated addDetail() calls in formatErrorContext #6782

@github-actions

Description

@github-actions

🔍 Duplicate Code Pattern: Duplicated addDetail() calls in formatErrorContext

Part of duplicate code analysis: #6781

Summary

The formatErrorContext function in internal/config/validation_schema.go contains identical addDetail() call pairs duplicated across two separate code blocks: a keyword-based switch statement (the primary path) and a series of message-based if strings.Contains(...) checks (the fallback path). Both blocks call addDetail() with the exact same key and message strings for 7 distinct error categories.

Duplication Details

Pattern: Identical addDetail() calls in keyword-switch and message-fallback blocks

  • Severity: Medium
  • Occurrences: 7 duplicated pairs (14 total addDetail() calls for 7 keywords)
  • Locations:
    • internal/config/validation_schema.go (lines 497–526) — keyword-based switch block
    • internal/config/validation_schema.go (lines 528–575) — message-based fallback if block
  • Code Sample:
// PRIMARY PATH (switch on keyword, lines 497-526)
switch keyword {
case "additionalProperties":
    addDetail("additionalProperties",
        "Details: Configuration contains field(s) that are not defined in the schema",
        "  → Check for typos in field names or remove unsupported fields")
case "type":
    addDetail("type",
        "Details: Type mismatch - the value type doesn't match what's expected",
        "  → Verify the value is the correct type (string, number, boolean, object, array)")
// ... 5 more identical cases
}

// FALLBACK PATH (if strings.Contains, lines 528-575) — exact same addDetail calls
if strings.Contains(msg, "additionalProperties") || strings.Contains(msg, "additional property") {
    addDetail("additionalProperties",
        "Details: Configuration contains field(s) that are not defined in the schema",
        "  → Check for typos in field names or remove unsupported fields")
}
if strings.Contains(msg, "expected") && (strings.Contains(msg, "but got") || strings.Contains(msg, "type")) {
    addDetail("type",
        "Details: Type mismatch - the value type doesn't match what's expected",
        "  → Verify the value is the correct type (string, number, boolean, object, array)")
}
// ... 5 more identical blocks

Impact Analysis

  • Maintainability: When error guidance text needs updating (e.g., improving a hint message), both the switch case and the corresponding if block must be updated in sync. Missing one creates inconsistent behavior between primary and fallback paths.
  • Bug Risk: Medium — the added deduplication map prevents double-output at runtime, but a future developer could inadvertently change one block without the other, resulting in different messages on the primary vs. fallback path.
  • Code Bloat: ~42 lines of duplicated code (30 in the switch + 12 key duplicated lines in the fallback)

Refactoring Recommendations

Option 1: Extract detailForKeyword helper (Recommended)

Extract the keyword→guidance mapping into a separate helper and call it from both the switch and fallback paths:

// detailForKeyword returns the (key, lines...) arguments for addDetail given a known keyword.
// Returns empty strings if the keyword is unknown.
func detailForKeyword(keyword string) (string, []string) {
    switch keyword {
    case "additionalProperties":
        return "additionalProperties", []string{
            "Details: Configuration contains field(s) that are not defined in the schema",
            "  → Check for typos in field names or remove unsupported fields",
        }
    case "type":
        return "type", []string{
            "Details: Type mismatch - the value type doesn't match what's expected",
            "  → Verify the value is the correct type (string, number, boolean, object, array)",
        }
    // ... other cases
    }
    return "", nil
}

// Then in formatErrorContext:
if k, lines := detailForKeyword(keyword); k != "" {
    addDetail(k, lines...)
}
// Fallback: map message keywords to the same helper
for _, candidate := range []string{"additionalProperties", "type", "enum", "required", "pattern", "range", "oneOf"} {
    if messageMatchesKeyword(msg, candidate) {
        if k, lines := detailForKeyword(candidate); k != "" {
            addDetail(k, lines...)
        }
    }
}

Option 2: Consolidate into a single lookup table

Define a []keywordRule slice with keyword, msgPatterns, and lines fields, then loop once over the rules applying both the switch and message-fallback logic without duplication.

Option 3: Evaluate necessity of fallback

Now that keywordFromLocation is the primary classification path, determine whether the message-based fallback if-blocks are still needed. If the keyword location reliably covers all cases, the fallback blocks can be removed entirely, eliminating the duplication.

Implementation Checklist

  • Review whether fallback message-based checks are still necessary
  • Extract detailForKeyword helper (or equivalent) to centralize guidance strings
  • Update formatErrorContext to call the helper from both paths
  • Verify existing tests in validation_schema_error_format_test.go still pass
  • Add/update tests to cover both keyword-location and message-fallback paths

Parent Issue

See parent analysis report: #6781
Related to #6781

Generated by Duplicate Code Detector · sonnet46 1.5M ·

  • expires on Jun 7, 2026, 3:40 AM UTC

Metadata

Metadata

Assignees

Type

No type
No fields configured for issues without a type.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions