{"record":{"id":"3bcc0deb7d764a33","repo":"caddyserver/caddy","slug":"encoder-already-added-s","errorCode":null,"errorMessage":"encoder already added: %s","messagePattern":"encoder already added: (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"modules/caddyhttp/encode/encode.go","lineNumber":209,"sourceCode":"\terr := next.ServeHTTP(w, r)\n\t// If there was an error, disable encoding completely\n\t// This prevents corruption when handle_errors processes the response\n\tif err != nil {\n\t\tif ew, ok := w.(*responseWriter); ok {\n\t\t\tew.disabled = true\n\t\t}\n\t}\n\n\treturn err\n}\n\nfunc (enc *Encode) addEncoding(e Encoding) error {\n\tae := e.AcceptEncoding()\n\tif ae == \"\" {\n\t\treturn fmt.Errorf(\"encoder does not specify an Accept-Encoding value\")\n\t}\n\tif _, ok := enc.writerPools[ae]; ok {\n\t\treturn fmt.Errorf(\"encoder already added: %s\", ae)\n\t}\n\tif enc.writerPools == nil {\n\t\tenc.writerPools = make(map[string]*sync.Pool)\n\t}\n\tenc.writerPools[ae] = &sync.Pool{\n\t\tNew: func() any {\n\t\t\treturn e.NewEncoder()\n\t\t},\n\t}\n\treturn nil\n}\n\n// openResponseWriter creates a new response writer that may (or may not)\n// encode the response with encodingName. The returned response writer MUST\n// be closed after the handler completes.\nfunc (enc *Encode) openResponseWriter(encodingName string, w http.ResponseWriter, isConnect bool) *responseWriter {\n\tvar rw responseWriter\n\treturn enc.initResponseWriter(&rw, encodingName, w, isConnect)","sourceCodeStart":191,"sourceCodeEnd":227,"githubUrl":"https://github.com/caddyserver/caddy/blob/50e54ee279aa1e504fe218ca49ab6ae16c100410/modules/caddyhttp/encode/encode.go#L191-L227","documentation":"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.","triggerScenarios":"Two encoder modules both claiming \"gzip\" loaded into the same encode config; e.g. a plugin that wraps gzip but reuses the same token.","commonSituations":"A custom re-implementation of an existing encoder that didn't pick a new token; two plugins colliding.","solutions":["Give each encoder a distinct AcceptEncoding() token","Remove one of the colliding encoders from the config","Check caddy list-modules for two modules registering overlapping encodings"],"exampleFix":"// before\nfunc (A) AcceptEncoding() string { return \"gzip\" } // collides with stdlib gzip\n// after\nfunc (A) AcceptEncoding() string { return \"gzip2\" }","handlingStrategy":"validation","validationCode":"// plugin self-check in tests: token not already used by stdlib\nfunc TestAcceptEncodingUnique(t *testing.T) {\n    for _, taken := range []string{\"gzip\", \"zstd\"} {\n        if MyEnc{}.AcceptEncoding() == taken { t.Fatal(\"token collision\") }\n    }\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Namespace custom encoder tokens to avoid collisions","Don't register two modules for the same encoding"],"tags":["caddy","encode","plugin","conflict"],"backgroundTag":null,"analyzedSha":"50e54ee279aa1e504fe218ca49ab6ae16c100410","analyzedAt":"2026-08-15T09:20:21.641Z","schemaVersion":2},"datasetVersion":"2026-08-16T03:17:38.424Z"}