{"record":{"id":"bb5a59fa87554cdc","repo":"honojs/hono","slug":"key-must-not-contain-r-or-n","errorCode":null,"errorMessage":"${key} must not contain \"\\r\" or \"\\n\"","messagePattern":"(.+?) must not contain \"\\\\r\" or \"\\\\n\"","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"src/helper/streaming/sse.ts","lineNumber":30,"sourceCode":"\nexport class SSEStreamingApi extends StreamingApi {\n  constructor(writable: WritableStream, readable: ReadableStream) {\n    super(writable, readable)\n  }\n\n  async writeSSE(message: SSEMessage) {\n    const data = await resolveCallback(message.data, HtmlEscapedCallbackPhase.Stringify, false, {})\n    const dataLines = (data as string)\n      .split(/\\r\\n|\\r|\\n/)\n      .map((line) => {\n        return `data: ${line}`\n      })\n      .join('\\n')\n\n    for (const key of ['event', 'id'] as const) {\n      const value = message[key]\n      if (value && /[\\r\\n]/.test(value)) {\n        throw new Error(`${key} must not contain \"\\\\r\" or \"\\\\n\"`)\n      }\n    }\n\n    const sseData =\n      [\n        message.event && `event: ${message.event}`,\n        dataLines,\n        message.id !== undefined && `id: ${message.id}`,\n        message.retry !== undefined && `retry: ${message.retry}`,\n      ]\n        .filter(Boolean)\n        .join('\\n') + '\\n\\n'\n\n    await this.write(sseData)\n  }\n}\n\nconst run = async (","sourceCodeStart":12,"sourceCodeEnd":48,"githubUrl":"https://github.com/honojs/hono/blob/e2740d5a1bd0b4254e517e3af8b60789284bc7bd/src/helper/streaming/sse.ts#L12-L48","documentation":"When serializing a Server-Sent Event, Hono validates that the `event` and `id` fields contain no CR or LF characters, because those would break the SSE wire format (each field must be a single `field: value` line). Violating input is rejected with this Error rather than emitting a corrupt stream.","triggerScenarios":"Passing `new SSEStreamingApi().sse({ event: ..., id: ... })` / streaming SSE messages where the `event` name or `id` string contains '\\n', '\\r', or a multi-line value (e.g. using a full message or JSON blob as the event name or id).","commonSituations":"Using the whole error message (which may be multi-line) as the SSE event name; using timestamps+uuid ids built from template strings that accidentally include newlines; forwarding untrusted user input into event/id; copy-pasting examples that put data in `event`.","solutions":["Keep `event` and `id` as short single-line tokens: 'message', 'update', a counter or hash-based id","Put arbitrary/multi-line content in the `data` field, which SSE encoding handles correctly","Sanitize before sending: `String(v).replace(/[\\r\\n]+/g, ' ')` on event/id","Add a lint/unit assertion that generated event names match /^[^\\r\\n]*$/"],"exampleFix":"// before\nawait sse.sse({\n  event: JSON.stringify(payload), // contains newlines -> throws\n  data: 'x',\n})\n\n// after\nawait sse.sse({\n  event: 'update',\n  id: String(seq),\n  data: JSON.stringify(payload),\n})","handlingStrategy":"validation","validationCode":"const cleanField = (v: string) => v.replace(/[\\r\\n]+/g, ' ')\n\nawait sse.sse({\n  event: cleanField(name).slice(0, 64),\n  id: cleanField(id),\n  data: JSON.stringify(payload),\n})","typeGuard":"const isSSEFieldSafe = (v: unknown): v is string => typeof v === 'string' && !/[\\r\\n]/.test(v)","tryCatchPattern":"try { await sse.sse(msg) } catch (e) { if (e instanceof Error && /must not contain/.test(e.message)) { await sse.sse({ ...msg, event: 'message', id: undefined }) } else throw e }","preventionTips":["Only put free-form content in `data`; keep event/id single-line tokens","Sanitize any user-derived value used as event or id","Unit-test SSE emitters with multi-line payloads"],"tags":["sse","streaming","validation","event-id","line-breaks"],"backgroundTag":"sse-invalid-field-characters","analyzedSha":"e2740d5a1bd0b4254e517e3af8b60789284bc7bd","analyzedAt":"2026-08-28T10:18:08.750Z","schemaVersion":2},"datasetVersion":"2026-08-28T11:17:15.048Z"}