{"record":{"id":"92736caafcb619a7","repo":"we-promise/sure","slug":"external-assistant-stream-exceeded-maximum-buffer","errorCode":null,"errorMessage":"External assistant stream exceeded maximum buffer size.","messagePattern":"External assistant stream exceeded maximum buffer size\\.","errorType":"exception","errorClass":"Assistant::Error","httpStatus":null,"severity":"error","filePath":"app/models/assistant/external/client.rb","lineNumber":83,"sourceCode":"  private\n\n    def stream_response(http, request, &block)\n      model = nil\n      buffer = +\"\"\n      done = false\n\n      http.request(request) do |response|\n        unless response.is_a?(Net::HTTPSuccess)\n          Rails.logger.warn(\"[External::Client] Upstream HTTP #{response.code}: #{response.body.to_s.truncate(500)}\")\n          raise Assistant::Error, \"External assistant returned HTTP #{response.code}.\"\n        end\n\n        response.read_body do |chunk|\n          break if done\n          buffer << chunk\n\n          if buffer.bytesize > MAX_SSE_BUFFER\n            raise Assistant::Error, \"External assistant stream exceeded maximum buffer size.\"\n          end\n\n          while (line_end = buffer.index(\"\\n\"))\n            line = buffer.slice!(0..line_end).strip\n            next if line.empty?\n            next unless line.start_with?(\"data:\")\n\n            data = line.delete_prefix(\"data:\")\n            data = data.delete_prefix(\" \") # SSE spec: strip one optional leading space\n\n            if data == \"[DONE]\"\n              done = true\n              break\n            end\n\n            parsed = parse_sse_data(data)\n            next unless parsed\n","sourceCodeStart":65,"sourceCodeEnd":101,"githubUrl":"https://github.com/we-promise/sure/blob/e69894adb92547273377398c15f45c979cd9416a/app/models/assistant/external/client.rb#L65-L101","documentation":"Raised by Assistant::External::Client#stream_response when the incremental SSE buffer exceeds MAX_SSE_BUFFER = 1 MB (app/models/assistant/external/client.rb:10). The buffer only drains when a newline-terminated line is sliced off, so it guards both against a single enormous SSE line and against upstreams that stream a giant payload with no newlines at all.","triggerScenarios":"Upstream ignores the Accept: text/event-stream request header and returns one multi-megabyte JSON body without newline line breaks, so buffer << chunk accumulates past 1 MiB before the `while (line_end = buffer.index(\"\\n\"))` loop ever consumes anything; or a single data: line (one huge JSON event with a very long content delta) crosses the 1 MB cap; or a non-SSE error page/HTML stream without newlines.","commonSituations":"Gateway in front of the agent that buffers the whole response and sends it in one shot; upstream returning full-completion JSON instead of token-streamed SSE; prompt/model misconfiguration making the model emit a single gigantic output; misrouted URL serving an HTML page.","solutions":["Confirm the upstream actually streams SSE: newline-terminated `data: {...}` lines ending with `data: [DONE]` — test with curl -N.","If the endpoint only returns non-streamed JSON, point the client at a streaming-capable route rather than raising the cap.","If legitimate single events exceed 1 MB (e.g. giant tool payloads in content), raise MAX_SSE_BUFFER in app/models/assistant/external/client.rb:10 — it is a class constant, not env-configurable.","Check the logged upstream path for HTML error pages (proxy 502 pages) that arrive without newlines."],"exampleFix":"# before (class constant, client.rb:10)\nMAX_SSE_BUFFER = 1_048_576 # 1 MB\n\n# after — raise the cap when legitimate events are larger\nMAX_SSE_BUFFER = 4_194_304 # 4 MB","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"begin\n  client.chat(messages: msgs) { |c| stream.write(c) }\nrescue Assistant::Error => e\n  raise unless e.message.include?(\"buffer size\")\n  # do not blind-retry: the upstream is sending non-SSE or oversized events\n  Rails.logger.error(\"SSE buffer cap hit — verify upstream streams newline-delimited events\")\n  raise\nend","preventionTips":["Verify the upstream emits newline-terminated data: lines (curl -N) — the buffer only drains on newlines.","Confirm the endpoint honors Accept: text/event-stream and does not fall back to one giant JSON body or an HTML error page.","If legitimate single events exceed 1 MB, raise MAX_SSE_BUFFER deliberately rather than catching the error repeatedly."],"tags":["streaming","sse","buffer-limit","assistant","memory-guard"],"backgroundTag":"payload-too-large","analyzedSha":"e69894adb92547273377398c15f45c979cd9416a","analyzedAt":"2026-08-21T18:22:41.165Z","schemaVersion":2},"datasetVersion":"2026-08-21T23:17:16.201Z"}