caddyserver/caddy · error

encoder already added: %s

Error message

encoder already added: %s

What it means

Returned by addEncoding when a second encoder registers the same Accept-Encoding token as one already in enc.writerPools. Each token may map to exactly one encoder per encode handler.

Source

Thrown at modules/caddyhttp/encode/encode.go:209

	err := next.ServeHTTP(w, r)
	// If there was an error, disable encoding completely
	// This prevents corruption when handle_errors processes the response
	if err != nil {
		if ew, ok := w.(*responseWriter); ok {
			ew.disabled = true
		}
	}

	return err
}

func (enc *Encode) addEncoding(e Encoding) error {
	ae := e.AcceptEncoding()
	if ae == "" {
		return fmt.Errorf("encoder does not specify an Accept-Encoding value")
	}
	if _, ok := enc.writerPools[ae]; ok {
		return fmt.Errorf("encoder already added: %s", ae)
	}
	if enc.writerPools == nil {
		enc.writerPools = make(map[string]*sync.Pool)
	}
	enc.writerPools[ae] = &sync.Pool{
		New: func() any {
			return e.NewEncoder()
		},
	}
	return nil
}

// openResponseWriter creates a new response writer that may (or may not)
// encode the response with encodingName. The returned response writer MUST
// be closed after the handler completes.
func (enc *Encode) openResponseWriter(encodingName string, w http.ResponseWriter, isConnect bool) *responseWriter {
	var rw responseWriter
	return enc.initResponseWriter(&rw, encodingName, w, isConnect)

View on GitHub (pinned to 50e54ee279)

Solutions

  1. Give each encoder a distinct AcceptEncoding() token
  2. Remove one of the colliding encoders from the config
  3. Check caddy list-modules for two modules registering overlapping encodings

Example fix

// before
func (A) AcceptEncoding() string { return "gzip" } // collides with stdlib gzip
// after
func (A) AcceptEncoding() string { return "gzip2" }
Defensive patterns

Strategy: validation

Validate before calling

// plugin self-check in tests: token not already used by stdlib
func TestAcceptEncodingUnique(t *testing.T) {
    for _, taken := range []string{"gzip", "zstd"} {
        if MyEnc{}.AcceptEncoding() == taken { t.Fatal("token collision") }
    }
}

Prevention

When it happens

Trigger: Two encoder modules both claiming "gzip" loaded into the same encode config; e.g. a plugin that wraps gzip but reuses the same token.

Common situations: A custom re-implementation of an existing encoder that didn't pick a new token; two plugins colliding.

Related errors


AI-assisted analysis of caddyserver/caddy@50e54ee279 (2026-08-15). Data as JSON: /api/errors/3bcc0deb7d764a33. Report an issue: GitHub.