{"id":"e963ef62f8117dac","repo":"gofiber/fiber","slug":"sse-invalid-id-w","errorCode":null,"errorMessage":"sse: invalid id: %w","messagePattern":"sse: invalid id: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"middleware/sse/event.go","lineNumber":44,"sourceCode":"\t// Name sets the SSE event field.\n\tName string\n\n\t// Retry sets the SSE retry field for this event.\n\tRetry time.Duration\n}\n\nfunc writeEvent(w *bufio.Writer, event Event, jsonMarshal ...utils.JSONMarshal) error {\n\tdata, err := eventData(event.Data, jsonMarshalOrDefault(jsonMarshal))\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tvar frame bytes.Buffer\n\n\tif event.ID != \"\" {\n\t\tid, err := sanitizeField(event.ID)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"sse: invalid id: %w\", err)\n\t\t}\n\t\tif id != \"\" {\n\t\t\tappendField(&frame, \"id\", id)\n\t\t}\n\t}\n\tif event.Name != \"\" {\n\t\tname, err := sanitizeField(event.Name)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"sse: invalid event: %w\", err)\n\t\t}\n\t\tif name != \"\" {\n\t\t\tappendField(&frame, \"event\", name)\n\t\t}\n\t}\n\tif event.Retry > 0 {\n\t\tappendField(&frame, \"retry\", utils.FormatInt(event.Retry.Milliseconds()))\n\t}\n\tif data.hasData {","sourceCodeStart":26,"sourceCodeEnd":62,"githubUrl":"https://github.com/gofiber/fiber/blob/9a4c7e57fe0b080a04235d28a4b0d2b4b353d58c/middleware/sse/event.go#L26-L62","documentation":"writeEvent rejects an Event.ID that contains a carriage return or line feed: in the SSE wire format a newline terminates a field, so an embedded CR/LF would corrupt the id field. sanitizeField returns errInvalidField and Fiber wraps it rather than emitting a broken frame.","triggerScenarios":"Passing sse.Event{ID: value} where value contains \\n or \\r - e.g. a multi-line string, base64 with line breaks, or unsanitized user input used as the last-event id.","commonSituations":"Using database/external values verbatim as the SSE id; copy-pasting multi-line identifiers; treating a free-text field as an id.","solutions":["Strip or replace CR and LF from the id before building the Event.","Validate that the id is a single line and reject/log multi-line values at the source."],"exampleFix":"// before\nstream.Event(sse.Event{ID: rawID, Data: payload}) // rawID may contain \\n\n\n// after\nstream.Event(sse.Event{ID: sanitizeSSEField(rawID), Data: payload})\n\nfunc sanitizeSSEField(s string) string {\n    s = strings.ReplaceAll(s, \"\\r\\n\", \"-\")\n    s = strings.ReplaceAll(s, \"\\r\", \"-\")\n    return strings.ReplaceAll(s, \"\\n\", \"-\")\n}","handlingStrategy":"validation","validationCode":"func validSSEID(id string) bool {\n    return !strings.ContainsAny(id, \"\\r\\n\")\n}\n// before sending:\nif !validSSEID(event.ID) {\n    event.ID = strings.NewReplacer(\"\\r\\n\", \"-\", \"\\r\", \"-\", \"\\n\", \"-\").Replace(event.ID)\n}","typeGuard":null,"tryCatchPattern":"if err := stream.Event(ev); err != nil {\n    if errors.Is(err, sse.ErrInvalidField) { // if exposed, else string-match\n        ev.ID = sanitizeSSEField(ev.ID)\n        return stream.Event(ev)\n    }\n}","preventionTips":["Never trust external strings as SSE id/event fields without sanitizing newlines.","Generate ids from single-line sources (UUID, counter, base64 RawStdEncoding)."],"tags":["sse","validation","fiber"],"analyzedSha":"9a4c7e57fe0b080a04235d28a4b0d2b4b353d58c","analyzedAt":"2026-08-04T21:44:03.395Z","schemaVersion":2}