Fix Higress 2.2.0 Wasm match_rule Reroute Bug — Troubleshoot & Patch


Higress Wasm plugin match_rule reroute issue (Higress 2.2.0) — diagnosis, fix, and workaround

Summary: If your Wasm plugin’s match_rule evaluation fails after a route reroute — commonly observed after dynamic routing changes in Higress 2.2.0 — the root cause is usually stale route/streamInfo caching inside the plugin. This article explains why it happens, gives a practical code fix pattern, tests to validate the fix, and short-term workarounds including rollback guidance. A link to the detailed issue report is available for reference.

What happens and why it breaks match_rule after reroute

Plugins that rely on route-derived data (match_rule, route metadata, or header-based routing decisions) commonly read route information from the stream context once and cache it for performance. When Higress or Envoy performs an internal reroute or updates the route table dynamically, that cached pointer or copy can become stale. The plugin then makes decisions using outdated RouteEntry fields which do not reflect the new routing state, causing match_rule evaluation to fail or make incorrect decisions.

This is not a Higress-specific philosophical bug — it’s a lifecycle mismatch: route objects in the proxy’s memory can be replaced or re-resolved while a stream is active, so long-lived cached references in Wasm code are unsafe unless explicitly revalidated. The symptom is deterministic: first request after route change may work, subsequent requests hit the stale cache and fail match_rule checks.

Typical indicators in logs include matched rules that contradict request headers, repeated calls to the plugin showing different expected route fields, or when enabling debug you see the streamInfo route differs from what the plugin previously cached.

Root cause and diagnosis checklist

Before changing code, confirm the root cause with this short checklist. Diagnosing correctly saves unnecessary rollbacks.

  • Confirm Higress version and timing of route changes — are dynamic reroutes happening at or near the time failures appear?
  • Check the plugin for any cached route objects, pointers, or fields read only at plugin initialization or the first request callback.
  • Enable detailed Wasm + Envoy debug logs for router and your plugin to see if streamInfo->route matches the real-time route.

Do not assume network-level bugs — if the only change correlating with failures is a config push (virtual host or route update), stale route info in the plugin is the most likely culprit.

Safe, practical code fix pattern (avoid stale route caching)

The safest fix is to stop caching route objects across events and always read the authoritative route data from the streamInfo or resolve it at the moment you need to evaluate match_rule. If you must cache for performance, cache only immutable pieces (e.g., literal strings) with a clear invalidation or version check tied to the route’s last-update timestamp or a change counter.

Below is a concrete, minimal pseudo-implementation (C++-style pseudocode compatible with proxy-wasm SDK patterns). The core idea: call getRouteEntry() each time you evaluate a match, and do not assume the pointer remains valid across reroutes.

// Pseudocode for proxy-wasm / Envoy Wasm plugin
// Key: do not store RouteEntry* across callbacks; always re-resolve.

void onRequestHeaders(int headers_length) {
  // Get headers necessary to identify request
  auto path = getRequestHeader(":path");
  auto host = getRequestHeader("host");

  // Resolve authoritative route entry at the moment of evaluation
  RouteEntryPtr routeEntry = streamInfo()->routeEntry(); // or getRouteEntry()
  if (!routeEntry) {
    // Fallback: compute matching rule by headers if routeEntry is missing
    routeEntry = resolveRouteFromHeaders(host, path);
  }

  if (!routeEntry) {
    logWarn("No route entry available; deferring match_rule evaluation");
    return;
  }

  // Use fresh routeEntry fields for match_rule logic
  auto metadata = routeEntry->metadata();
  bool matched = evaluateMatchRule(metadata, requestHeaders());
  if (matched) {
    // Proceed with plugin behavior
    injectHeader("x-matched", "true");
  } else {
    injectHeader("x-matched", "false");
  }
}

If your language SDK doesn’t expose a direct streamInfo->routeEntry() API name, use the equivalent call provided by your proxy-wasm SDK (getRoute(), get_route(), or request stream info helpers). The essential requirement: obtain the authoritative route at evaluation time; never reuse an earlier object that could have been replaced by a reroute.

Test & validate the fix

After applying the change above, run these tests to confirm the solution:

  • Simulate a route update (config push or dynamic reroute) while long-lived streams exist. Re-run the requests that failed previously and verify match_rule behavior matches the new route.
  • Monitor plugin logs (debug) to ensure each evaluation uses the freshly resolved routeEntry and does not rely on a cached pointer.
  • Run a small load test to check for performance regressions — resolving the route per-evaluation adds cost; if it’s significant, implement a light-weight invalidation scheme (see below).

If load tests show unacceptable overhead, implement a versioned cache: store a small route-version token (routeEntry->version or a change counter exposed by Envoy/Higress) and only refresh when that token changes. Do not cache raw pointers long-term.

Workarounds and downgrade guidance

If you cannot immediately land a code change into production, you can use one of these temporary mitigations while you prepare a proper patch:

  • Revert the offending Wasm plugin change that began caching route information, if applicable — quickest and safest if the plugin behavior regressed after a plugin update.
  • Roll back Higress to the last known good version that did not trigger reroute behavior in your environment (for example, a pre-2.2.0 release you validated). Test the rollback in staging first; rolling back control plane components may affect other features.
  • Disable dynamic reroute features or config pushes during the window where the plugin cannot be updated, if that is operationally acceptable.

Note: Downgrading a control plane component should be done carefully. Make a backup of current configs and perform canary rollouts. If you need step-by-step rollback commands for your deployment method (Helm, operator, or plain manifests), follow your deployment tool’s recommended rollback procedure.

Best practices to avoid similar issues

Adopt these simple rules for any future Wasm plugin development that depends on proxy runtime objects:

1) Avoid caching runtime objects that the proxy or control plane can replace. 2) If caching is necessary for performance, rely on an explicit version token or change counter for invalidation. 3) Add debug logs showing the route version or route id at match time so regressions are visible immediately in production logs.

Also add unit tests and an integration test that forces a route reload and validates plugin behavior across that reload. The integration test should be part of your CI pipeline so this class of regression is blocked early.

When you need an immediate code example for Rust or Go

If you are writing the plugin in Rust or Go via proxy-wasm SDKs, the same rules apply: call the SDK routine that returns the current route or request stream info at evaluation time. For example, in Go you would call proxywasm.GetStreamInfo().Route (SDK names vary) rather than caching the route pointer returned at plugin init.

Example Go-style pseudocode:

func OnHttpRequestHeaders(ctx context.Context, numHeaders int) types.Action {
    route := proxywasm.GetStreamInfo(ctx).Route
    if route == nil {
        // fallback
    }
    matched := evaluateMatchRule(route.Metadata, reqHeaders)
    if matched {
       proxywasm.AddHttpResponseHeader("x-matched","true")
    }
    return types.Continue
}

Suggested micro-markup (FAQ and Article) for SEO and rich results

To improve search visibility and enable a FAQ rich snippet, add structured data in JSON-LD. Below is a ready-to-publish FAQ schema. Insert it into the page <head> or the end of the <body>.

{
  "@context": "https://schema.org",
  "@type": "FAQPage",
  "mainEntity": [
    {
      "@type": "Question",
      "name": "Why does Higress Wasm plugin match_rule fail after reroute?",
      "acceptedAnswer": {
        "@type": "Answer",
        "text": "Because the plugin cached route/streamInfo objects that became stale when Higress/Envoy updated routing. The solution is to re-resolve the route from stream info at evaluation time or implement proper cache invalidation."
      }
    },
    {
      "@type": "Question",
      "name": "How do I fix stale streamInfo route retrieval in a Wasm plugin?",
      "acceptedAnswer": {
        "@type": "Answer",
        "text": "Stop caching route objects across callbacks. Call the SDK's streamInfo->routeEntry() or equivalent each time you evaluate match_rule, or use a versioned cache invalidated on route change."
      }
    },
    {
      "@type": "Question",
      "name": "Is downgrading Higress a safe workaround?",
      "acceptedAnswer": {
        "@type": "Answer",
        "text": "Downgrading can be a valid temporary mitigation, but it carries operational risk. Prefer reverting the plugin or disabling dynamic reroute until the plugin is patched. If you must downgrade, test in staging and follow your deployment tool's rollback procedures."
      }
    }
  ]
}
Backlink to issue context: For logs, reproducer data, and configuration examples referenced in this article, see the detailed issue report: Higress Wasm plugin reroute bug (issue #3571).

FAQ

1. Why does match_rule stop working after a reroute?

Because the Wasm plugin used a cached route object or pointer that became invalid when Higress/Envoy updated routing during the reroute. The plugin then evaluates match_rule against stale data. The fix is to re-resolve the route from stream info every time you evaluate rules or use a robust invalidation token tied to route version changes.

2. What is the simplest code change that usually fixes the issue?

Always obtain the current route at evaluation time (e.g., streamInfo()->routeEntry() or your SDK’s equivalent) and avoid persisting route pointers between callbacks. If you must cache for performance, cache a version token and refresh the cached data when the token changes.

3. Can I safely roll back Higress as a workaround?

Yes, rolling back to a last-known-good release can be a temporary workaround, but it may affect other features or compatibility. Prefer reverting the plugin change or disabling dynamic reroute until you can deploy the fix. If rolling back, validate the full system in staging and follow your standard rollback procedures.

Semantic core (keyword clusters)

Primary keywords:

  • Higress Wasm plugin match_rule issue
  • Wasm plugin reroute bug Higress 2.2.0
  • match_rule failure after reroute
  • streamInfo route stale information

Secondary keywords and LSI phrases:

  • Higress plugin rerouting problem
  • code fix for Wasm route retrieval
  • plugin match_rule configuration error
  • Higress 2.2.0 route stale streamInfo
  • proxy-wasm getRouteEntry stale
  • Envoy routeEntry stale after reroute
  • resolve route from headers
  • route cache invalidation token

Clarifying long-tail queries and intent-based phrases:

  • How to fix Wasm plugin match_rule after reroute in Higress
  • workaround downgrade Higress 2.2.0 reroute bug
  • sample code for retrieving current route in proxy-wasm
  • best practices for caching route in Wasm plugin
  • diagnose streamInfo route mismatch after reroute

If you’d like, I can: 1) produce a patch PR (diff) for your plugin repository implementing the fix pattern above; 2) craft a CI integration test that simulates route reloads; or 3) generate Helm rollback commands for a specific Higress chart version. Tell me which you prefer.