Skip to content

Conversation

@MaciekMis
Copy link
Contributor

@MaciekMis MaciekMis commented Jan 30, 2026

Requests to APIs return Not Found errors even if they exist. The behavior is different between Classic and OAS APIs depending on whether custom domains are enabled.

This affects organizations using multiple APIs with similar path prefixes

Legitimate API requests return 404 Not Found errors

Description

Related Issue

Motivation and Context

How This Has Been Tested

Screenshots (if appropriate)

Types of changes

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to change)
  • Refactoring or add test (improvements in base code or adds test coverage to functionality)

Checklist

  • I ensured that the documentation is up to date
  • I explained why this PR updates go.mod in detail with reasoning why it's required
  • I would like a code coverage CI quality gate exception and have explained why

Ticket Details

TT-15182
Status In Dev
Summary APIs returning Not Found error, with different behavior between Classic and OAS APIs if custom domains are enabled

Generated at: 2026-01-30 14:07:31

@probelabs
Copy link

probelabs bot commented Jan 30, 2026

This pull request resolves a critical routing bug where APIs with similar listen path prefixes (e.g., /api and /api-plus) would conflict, causing legitimate requests to fail with a 404 Not Found error. The issue stemmed from the gateway's use of a broad PathPrefix matcher that would incorrectly route requests for a more specific path to the handler for a shorter, matching prefix.

The fix introduces a more precise matching mechanism that is activated when HttpServerOptions.EnableStrictRoutes is enabled. Instead of the greedy PathPrefix, a custom matcher function is used. This new matcher validates that an incoming request path is either an exact match for the prefix or a sub-path (i.e., followed by a /). This ensures that requests are routed to the correct API, resolving the ambiguity between similar prefixes. The change is accompanied by a new integration test that validates the fix for this specific scenario.

Files Changed Analysis

  • gateway/api_loader.go: Contains the core logic change. The standard router.PathPrefix(prefix) is conditionally replaced with a custom MatcherFunc that implements stricter route matching when HttpServerOptions.EnableStrictRoutes is true. This is a targeted change of 16 additions and 1 deletion.
  • gateway/api_loader_test.go: A new test, TestListenPathConflictWhenCustomDomainIsDisabled, has been added. It simulates the scenario of multiple APIs with overlapping path prefixes and asserts that each request is routed to the correct backend, confirming the fix and preventing regressions. This accounts for 64 additions.

Architecture & Impact Assessment

  • What this PR accomplishes: It fixes a bug that caused incorrect routing and 404 errors for APIs with similar listen paths, improving the reliability and predictability of the gateway's routing engine.
  • Key technical changes: The primary change is the replacement of mux.Router.PathPrefix with a custom mux.MatcherFunc. This function implements a stricter path matching algorithm that correctly differentiates between similar prefixes (e.g., /api vs. /api-plus).
  • Affected system components: The change directly impacts the Gateway's HTTP Router within the loadHTTPService function. The impact is controlled by the EnableStrictRoutes configuration setting, meaning existing deployments that do not use this option will not be affected, ensuring backward compatibility.

Routing Logic Flow

graph TD
    subgraph "Old Logic (Greedy PathPrefix)"
        A[Request for /api-plus] --> B{Router};
        B --> C["/api handler (matches first)"];
        B --> D["/api-plus handler (also matches)"];
        C --> E[Incorrect Route / 404];
    end

    subgraph "New Logic (Strict MatcherFunc)"
        F[Request for /api-plus] --> G{Router};
        G --|/api|--> route(route) --> H{"Path == \"/api\" OR HasPrefix \"/api/\"?"};
        H -- No --> I["/api-plus handler"];
        I --> J[Correct Route];
    end
Loading

Scope Discovery & Context Expansion

The changes are well-localized to the gateway's API loading and routing logic. The key contextual element is the HttpServerOptions.EnableStrictRoutes flag, which gates this new behavior, making it an opt-in feature. The logic explicitly falls back to the old PathPrefix method if this flag is disabled. The addition of a targeted integration test in gateway/api_loader_test.go confirms the fix and its scope without requiring further code exploration.

Metadata
  • Review Effort: 2 / 5
  • Primary Label: bug

Powered by Visor from Probelabs

Last updated: 2026-01-30T14:10:39.348Z | Triggered by: pr_updated | Commit: 6229830

💡 TIP: You can chat with Visor using /visor ask <your question>

@github-actions
Copy link
Contributor

API Changes

no api changes detected
@probelabs
Copy link

probelabs bot commented Jan 30, 2026

✅ Security Check Passed

No security issues found – changes LGTM.

Architecture Issues (1)

Severity Location Issue
🟢 Info gateway/api_loader.go:899-908
The logic for determining when to use the strict path matcher and the matcher implementation itself are defined inline within the `loadHTTPService` function's loop. This makes the code harder to read and test in isolation. This logic could be extracted into helper functions.
💡 SuggestionConsider extracting the condition check and the matcher creation into separate helper functions. For example, a function `isSimplePrefix(prefix string) bool` could check if the prefix is suitable for the new matcher, and another function `newStrictPrefixMatcher(prefix string) mux.MatcherFunc` could create the matcher. This would simplify the main loop in `loadHTTPService` and improve code organization.

✅ Performance Check Passed

No performance issues found – changes LGTM.

Quality Issues (2)

Severity Location Issue
🟢 Info gateway/api_loader.go:918
The comment `// explicitRouteSubpaths as a fallback for backward compatibility` is misleading. The function `explicitRouteSubpaths` is a handler wrapper that enforces exact path matching when `EnableStrictRoutes` is false, and is a pass-through when it's true. It's part of the legacy routing logic rather than a fallback for the new subrouter creation logic.
💡 SuggestionUpdate the comment to more accurately describe the role of `explicitRouteSubpaths`, which is to provide the legacy exact-match behavior when strict routing is disabled. For example: `// explicitRouteSubpaths wraps the handler to enforce legacy exact-match behavior when strict routing is disabled.`
🟢 Info gateway/api_loader_test.go:9747-9752
The test uses automatically generated, non-descriptive strings for `listenPaths` (e.g., `/caas2itsamu0456n07gfe`). While functionally correct, this makes the test's intent less clear at a glance. Using more semantic and human-readable paths would improve readability and maintainability.
💡 SuggestionReplace the generated strings with clearer, more illustrative examples that demonstrate the prefix conflict, such as `"/api"`, `"/api-v2"`, and `"/api-extra"`. This would make the purpose of the test immediately obvious to future readers.

Powered by Visor from Probelabs

Last updated: 2026-01-30T14:10:42.292Z | Triggered by: pr_updated | Commit: 6229830

💡 TIP: You can chat with Visor using /visor ask <your question>

…nd-error-with-different-behavior-between-classic-and-oas-ap-is-if-custom-domains-are-enabled' into TT-15182-ap-is-returning-not-found-error-with-different-behavior-between-classic-and-oas-ap-is-if-custom-domains-are-enabled
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

2 participants