jaegertracing/jaeger · warning

access denied

Error message

access denied

What it means

ErrAccessDenied is the public sentinel that query interceptor implementations wrap when a trace query is refused on access-control grounds. The HTTP gateway and gRPC layers detect errors.Is(err, ErrAccessDenied) and map it to HTTP 403 / gRPC PERMISSION_DENIED instead of a generic 500.

Source

Thrown at components/extension/jaegerquery/queryinterceptor/interceptor.go:48

// storage protocol carry, so a change to the AST is a change to this contract — which is why
// the AST lives in a public, versioned module.
package queryinterceptor

import (
	"context"
	"errors"
	"time"

	"go.opentelemetry.io/collector/pdata/ptrace"

	expression "github.com/jaegertracing/jaeger-idl/query/expression/v1"
)

// ErrAccessDenied is the sentinel that interceptor implementations wrap
// when the caller's query is refused on access-control grounds. The API
// layers map it to HTTP 403 / gRPC PERMISSION_DENIED instead of a
// generic server error.
var ErrAccessDenied = errors.New("access denied")

// Query is the public view of a trace-search query passed to Interceptor.OnQuery.
//
// EXPERIMENTAL: this type and the Interceptor contract it belongs to may change or be removed
// in any release, without a deprecation period. Filter in particular is an RFC 0005 filter AST,
// which that RFC is still moving through its milestones, so an implementation should expect to
// be updated alongside jaeger-query rather than to keep compiling against a stable shape.
//
// Every predicate is in Filter, including the ones a caller sent as the older scalar search
// fields: jaeger-query expresses a service, an operation name, a tag and a duration bound as
// filter predicates before an interceptor sees them, so an implementation reads and rewrites
// one thing rather than a filter plus four fields that can say the same in two ways. The
// remaining fields are the envelope, which no predicate lives in.
type Query struct {
	// Filter is the query's predicates as a boolean-valued expression (RFC 0005 §6), or nil
	// when the search asks for a time range and nothing else. Nil rather than an empty
	// conjunction, because `and` takes two arguments or more, so there is no expression that
	// says "match everything".

View on GitHub (pinned to 806f444784)

Solutions

  1. If the denial is unexpected, inspect the configured query interceptor implementation and its access-control rules to see why the caller was refused
  2. If you implement an interceptor, always wrap your refusal error with ErrAccessDenied (`fmt.Errorf("trace not allowed for tenant %s: %w", t, ErrAccessDenied)`) so clients get 403, not 500
  3. Use errors.Is(err, queryinterceptor.ErrAccessDenied) in callers/tests to classify the failure rather than string matching

Example fix

// before
return nil, fmt.Errorf("access to trace %s denied for tenant %s", traceID, tenant)
// after
return nil, fmt.Errorf("access to trace %s denied for tenant %s: %w", traceID, tenant, queryinterceptor.ErrAccessDenied)
Defensive patterns

Strategy: type-guard

Validate before calling

// in interceptor implementation, ensure refusal errors wrap the sentinel
if !allowed(req) {
    return fmt.Errorf("query refused: %w", queryinterceptor.ErrAccessDenied)
}

Type guard

func isAccessDenied(err error) bool {
    return errors.Is(err, queryinterceptor.ErrAccessDenied)
}

Try / catch

resp, err := queryService.FindTraces(ctx, req)
if err != nil {
    if errors.Is(err, queryinterceptor.ErrAccessDenied) {
        return status.Error(codes.PermissionDenied, "access denied")
    }
    return status.Error(codes.Internal, err.Error())
}

Prevention

When it happens

Trigger: An Interceptor.OnQuery (or OnGetTrace etc.) implementation returns an error wrapping ErrAccessDenied via fmt.Errorf("...: %w", queryinterceptor.ErrAccessDenied); tryHandleError then converts it to a 403 status response.

Common situations: RBAC/tenant filtering middleware rejecting queries for traces outside the caller's allowed scope; security policies denying access to specific operations; tests asserting the sentinel mapping via TestAsStatusError / TestHTTPGatewayTryHandleError.

Understand the failure class

Related errors


AI-assisted analysis of jaegertracing/jaeger@806f444784 (2026-09-01). Data as JSON: /api/errors/9860f647804534be. Report an issue: GitHub.