{"record":{"id":"b07bb5c2b697e27c","repo":"amir20/dozzle","slug":"err-error-b07bb5","errorCode":null,"errorMessage":"err.Error()","messagePattern":"err\\.Error\\(\\)","errorType":"http","errorClass":null,"httpStatus":500,"severity":"error","filePath":"internal/web/events.go","lineNumber":27,"sourceCode":"\t\"github.com/amir20/dozzle/internal/container\"\n\tdocker_support \"github.com/amir20/dozzle/internal/support/docker\"\n\tsupport_web \"github.com/amir20/dozzle/internal/support/web\"\n\t\"github.com/amir20/dozzle/types\"\n\t\"github.com/rs/zerolog/log\"\n)\n\nconst (\n\teventBufferSize = 64\n\tstatBufferSize  = 128\n\t// minimum gap between retries of a host whose container list failed to refresh\n\tstaleHostRetryInterval = 5 * time.Second\n)\n\nfunc (h *handler) streamEvents(w http.ResponseWriter, r *http.Request) {\n\tsseWriter, err := support_web.NewSSEWriter(r.Context(), w, r)\n\tif err != nil {\n\t\tlog.Error().Err(err).Msg(\"error creating sse writer\")\n\t\thttp.Error(w, err.Error(), http.StatusInternalServerError)\n\t\treturn\n\t}\n\tdefer sseWriter.Close()\n\n\t// buffered so a momentarily slow client can't stall the shared per-host store loop,\n\t// which broadcasts to every subscriber with a blocking send\n\tevents := make(chan container.ContainerEvent, eventBufferSize)\n\tstats := make(chan container.ContainerStat, statBufferSize)\n\tavailableHosts := make(chan container.Host)\n\n\th.hostService.SubscribeEventsAndStats(r.Context(), events, stats)\n\th.hostService.SubscribeAvailableHosts(r.Context(), availableHosts)\n\n\tuserLabels := h.config.Labels\n\tif h.config.Authorization.Provider != NONE {\n\t\tuser := auth.UserFromContext(r.Context())\n\t\tif user.ContainerLabels.Exists() {\n\t\t\tuserLabels = user.ContainerLabels","sourceCodeStart":9,"sourceCodeEnd":45,"githubUrl":"https://github.com/amir20/dozzle/blob/d9463cbe21874e44ab79db6fa63e746ca7d22928/internal/web/events.go#L9-L45","documentation":"streamEvents fails with a 500 when support_web.NewSSEWriter cannot initialize the Server-Sent Events writer, most commonly because the http.ResponseController/Flusher or streaming is unavailable (e.g. response already hijacked/committed, or a proxy interferes with the connection setup). The raw error is returned in the body.","triggerScenarios":"GET /api/events/stream when the underlying response writer cannot be set up for SSE streaming (flusher unavailable, context already canceled, or the connection broke during setup).","commonSituations":"Reverse proxies buffering responses or rejecting upgrade-style streaming; client disconnecting exactly during connect; HTTP/1.0 clients or gateways that do not support chunked streaming.","solutions":["Configure the reverse proxy to disable response buffering for /api/ endpoints (proxy_buffering off / X-Accel-Buffering: no).","Retry the /api/events/stream connection; transient disconnects are handled by the frontend reconnect logic.","Ensure clients connect over HTTP/1.1+ or HTTP/2 where streaming is supported."],"exampleFix":null,"handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":"const es = new EventSource('/api/events/stream');\nes.onerror = () => { es.close(); setTimeout(connect, backoffMs()); };","preventionTips":["Disable proxy buffering for /api/ SSE endpoints.","Avoid proxies that terminate streaming connections aggressively.","Implement exponential backoff reconnects on the client."],"tags":["sse","http","streaming","proxy"],"backgroundTag":"http-request-failed","analyzedSha":"d9463cbe21874e44ab79db6fa63e746ca7d22928","analyzedAt":"2026-09-07T10:08:55.855Z","contentChangedAt":"2026-09-07T10:08:55.855Z","schemaVersion":2},"datasetVersion":"2026-09-16T04:17:20.429Z"}