istio/istio · error · ConfigError

TranslationError

TranslationError

Error message

failed to parse request timeout: %w

What it means

ApplyTimeouts parses HTTPRouteRule.spec.timeouts.request (a GEP-2257 duration string) with Go's time.ParseDuration. Strings Go cannot parse — missing unit ('30'), unknown unit ('1sec'), embedded spaces ('1 h'), or garbage — return this wrapped error, which ConvertHTTPRouteToAgw surfaces as a TranslationError condition on the route. Note Go is more permissive than GEP-2257 (it accepts '1m30s', '1.5h'), so values that parse here but violate GEP-2257 are accepted, while anything Go rejects fails.

Source

Thrown at pilot/pkg/config/kube/agentgateway/routes.go:125

		})
	}
	return nil
}

// ApplyTimeouts applies timeouts to an agw route
func ApplyTimeouts(rule *gatewayv1.HTTPRouteRule, route *api.Route) error {
	if rule == nil || rule.Timeouts == nil {
		return nil
	}
	if route.TrafficPolicies == nil {
		route.TrafficPolicies = []*api.TrafficPolicySpec{}
	}
	var reqDur, beDur *durationpb.Duration

	if rule.Timeouts.Request != nil {
		d, err := time.ParseDuration(string(*rule.Timeouts.Request))
		if err != nil {
			return fmt.Errorf("failed to parse request timeout: %w", err)
		}
		if d != 0 {
			// "Setting a timeout to the zero duration (e.g. "0s") SHOULD disable the timeout"
			// However, agentgateway already defaults to no timeout, so only set for non-zero
			reqDur = durationpb.New(d)
		}
	}
	if rule.Timeouts.BackendRequest != nil {
		d, err := time.ParseDuration(string(*rule.Timeouts.BackendRequest))
		if err != nil {
			return fmt.Errorf("failed to parse backend request timeout: %w", err)
		}
		if d != 0 {
			// "Setting a timeout to the zero duration (e.g. "0s") SHOULD disable the timeout"
			// However, agentgateway already defaults to no timeout, so only set for non-zero
			beDur = durationpb.New(d)
		}
	}

View on GitHub (pinned to 8dc789c5cf)

Solutions

  1. Use a valid duration with an explicit unit: '10s', '1m', '500ms', '1.5h'
  2. If the value comes from templating, ensure the unit is part of the template, not the variable: timeout: "{{ .timeoutSeconds }}s"
  3. Apply routes with server-side dry-run so the CRD pattern catches malformed durations before istiod translates them

Example fix

# before
spec:
  rules:
  - timeouts:
      request: 30
# after
spec:
  rules:
  - timeouts:
      request: 30s
Defensive patterns

Strategy: validation

Validate before calling

if rule.Timeouts != nil && rule.Timeouts.Request != nil {
    if _, err := time.ParseDuration(string(*rule.Timeouts.Request)); err != nil {
        return fmt.Errorf("timeouts.request %q is not a valid duration (use e.g. \"10s\", \"1m\"): %w", *rule.Timeouts.Request, err)
    }
}

Type guard

func isParseableGEPDuration(s string) bool {
    _, err := time.ParseDuration(s)
    return err == nil
}

Try / catch

if err := agentgateway.ApplyTimeouts(&rule, route); err != nil {
    if strings.Contains(err.Error(), "request timeout") {
        // reject/fix the manifest: timeouts.request is not a valid duration
    }
    return err // propagate as TranslationError on the route status
}

Prevention

When it happens

Trigger: An HTTPRoute rule with timeouts.request set to a non-Go-duration string, e.g. "30", "1sec", "second", "1 h", "PT30S" (ISO-8601 by mistake), or "500ms...". The v1 CRD pattern normally rejects these at admission, so hitting this means the route was created programmatically or with relaxed CRDs.

Common situations: Pasting ISO-8601 durations from other systems; numeric-only values from templating ({{ .timeout }} without a unit suffix); generated routes from internal platforms; hand-built HTTPRoute structs in Go.

Understand the failure class

Related errors


AI-assisted analysis of istio/istio@8dc789c5cf (2026-08-15). Data as JSON: /api/errors/4e0e5f450ce5444c. Report an issue: GitHub.