grpc/grpc-go · error

extproc: input metadata is nil

Error message

extproc: input metadata is nil

What it means

Raised by (*HeaderMutationRules).ApplyAdditions (extconfig.go:120) when the input metadata argument is nil. The method's contract (documented at extconfig.go:114 'The input metadata must not be nil') requires a non-nil metadata.MD to mutate; a nil input is a programmer error in the caller. In the shipped filter, ApplyAdditions is called from extproc applyMutations (ext_proc.go:1377) with the stream's metadata, which is expected to be non-nil.

Source

Thrown at internal/xds/httpfilter/extconfig.go:120

// key and value, and checks if the mutation is permitted by the AllowExpr and
// DisallowExpr regular expressions.
//
// The following headers are always ignored:
// - Pseudo-headers (keys starting with ':').
// - The 'host' header.
// - Headers with non-lowercase keys.
// - Headers with keys or values exceeding 16384 bytes.
//
// If a mutation is disallowed and DisallowIsError is true, an error is
// returned. Otherwise, the disallowed mutation is silently ignored.
//
// The input metadata must not be nil.
func (hmr *HeaderMutationRules) ApplyAdditions(hvos []*v3corepb.HeaderValueOption, input metadata.MD) error {
	if hmr == nil {
		hmr = &HeaderMutationRules{}
	}
	if input == nil {
		return fmt.Errorf("extproc: input metadata is nil")
	}
	if hmr.DisallowAll {
		return nil
	}

	for _, hvo := range hvos {
		header := hvo.GetHeader()
		key := header.GetKey()
		if len(key) == 0 || key[0] == ':' || key == "host" || key != strings.ToLower(key) || len(key) > 16384 {
			continue
		}

		value := header.GetValue()
		if strings.HasSuffix(key, "-bin") {
			value = string(header.GetRawValue())
		}
		if len(value) > 16384 {
			continue

View on GitHub (pinned to 03255a9237)

Solutions

  1. Ensure the metadata.MD passed to ApplyAdditions is always initialized — use metadata.MD{} (or the actual received headers) rather than a nil map.
  2. If you call applyMutations/ApplyAdditions directly, allocate md := metadata.MD{} before the call when no real headers exist.
  3. For extproc, confirm the stream path that reached applyMutations actually obtained outgoing/incoming metadata; a nil there indicates an upstream initialization bug to file against gRPC.

Example fix

// before
var md metadata.MD         // nil map
err := hmr.ApplyAdditions(setHeaders, md)

// after
md := metadata.MD{}       // non-empty, mutable map
err := hmr.ApplyAdditions(setHeaders, md)
Defensive patterns

Strategy: validation

Validate before calling

// Guard the ApplyAdditions contract (extconfig.go:119-121).
func safeApplyAdditions(hmr *httpfilter.HeaderMutationRules, hvos []*v3corepb.HeaderValueOption, md metadata.MD) error {
    if md == nil {
        md = metadata.MD{} // never pass nil; method requires non-nil input
    }
    return hmr.ApplyAdditions(hvos, md)
}

Prevention

When it happens

Trigger: ApplyAdditions is invoked with input == nil. In production this means the filter implementation passed a nil metadata.MD into applyMutations; in tests/tools it means a direct caller forgot to allocate metadata before calling.

Common situations: A direct unit test of HeaderMutationRules passes nil for the metadata; a custom filter integration that constructs a HeaderMutationRules and calls ApplyAdditions on a response path where no metadata was received yet; a regression in the extproc stream handling that fails to lazily initialize metadata.

Related errors


AI-assisted analysis of grpc/grpc-go@03255a9237 (2026-08-07). Data as JSON: /api/errors/e3240a718f8c12cd. Report an issue: GitHub.