{"record":{"id":"db1f404d22401b2c","repo":"nestjs/nest","slug":"you-must-return-an-observable-stream-to-use-server","errorCode":null,"errorMessage":"You must return an Observable stream to use Server-Sent Events (SSE).","messagePattern":"You must return an Observable stream to use Server-Sent Events \\(SSE\\)\\.","errorType":"exception","errorClass":"ReferenceError","httpStatus":null,"severity":"error","filePath":"packages/core/router/router-response-controller.ts","lineNumber":294,"sourceCode":"          if (closeRequested) {\n            settled = true;\n            endStream();\n            response.end();\n            resolve();\n            return;\n          }\n\n          settled = true;\n          finalize();\n          endStream();\n          reject(err);\n        });\n    });\n  }\n\n  private assertObservable(value: any) {\n    if (!isObservable(value)) {\n      throw new ReferenceError(\n        'You must return an Observable stream to use Server-Sent Events (SSE).',\n      );\n    }\n  }\n\n  private getOrCreateAbortController(\n    request: IncomingMessage,\n  ): AbortController {\n    const carrier = request as IncomingMessage & {\n      [SSE_ABORT_CONTROLLER]?: AbortController;\n    };\n    if (!carrier[SSE_ABORT_CONTROLLER]) {\n      carrier[SSE_ABORT_CONTROLLER] = new AbortController();\n    }\n    return carrier[SSE_ABORT_CONTROLLER];\n  }\n}\n","sourceCodeStart":276,"sourceCodeEnd":312,"githubUrl":"https://github.com/nestjs/nest/blob/dd75d7bd8c5e88048587e6768d36eb695f3e7a25/packages/core/router/router-response-controller.ts#L276-L312","documentation":"An `@Sse()` route handler must return an RxJS Observable — the framework subscribes to it and streams each emitted value as a Server-Sent Event, completing the response when the observable completes. If the handler returns a Promise, array, or plain value, the response controller throws ReferenceError 'You must return an Observable stream to use Server-Sent Events (SSE)' as soon as it inspects the return value.","triggerScenarios":"The @Sse() method is declared `async` (async functions always return a Promise) or returns `Promise.from(...)`, a plain object, an EventEmitter, or `of(...).toPromise()`; a plain `@Get()` handler is converted to @Sse() without rewriting the body; mixing SSE push semantics with one-shot database results.","commonSituations":"Adding realtime notifications/progress endpoints; developers used to returning arrays/objects from handlers; wrapping event emitters with `fromEvent` forgotten; TypeScript not flagging it because the method was typed loosely.","solutions":["Make the handler synchronous and return an Observable, e.g. `interval(1000).pipe(map(() => ({ data: tick })))`.","Bridge event sources with RxJS: `fromEvent(emitter, 'update')` or `new Observable(subscriber => ...)`, and map payloads to `{ data: ... }` message shape.","Remove `async` from the @Sse() method signature — a Promise is never an acceptable substitute.","Type the return as `Observable<MessageEvent>` so the compiler rejects invalid returns."],"exampleFix":"// before\n@Sse('updates')\nasync updates() {           // Promise -> ReferenceError\n  return this.db.poll();\n}\n\n// after\nimport { interval } from 'rxjs';\nimport { map } from 'rxjs/operators';\n\n@Sse('updates')\nupdates(): Observable<MessageEvent> {\n  return interval(1000).pipe(map(() => ({ data: { hello: 'world' } })));\n}","handlingStrategy":"type-guard","validationCode":"// Guarantee an Observable leaves the handler, whatever the source is\nimport { isObservable, of, from, Observable } from 'rxjs';\n\n@Sse('updates')\nupdates(): Observable<MessageEvent> {\n  const source = this.buildSource(); // may be value, array or promise\n  if (isObservable(source)) return source as Observable<MessageEvent>;\n  if (Array.isArray(source)) return from(source) as Observable<MessageEvent>;\n  return from(Promise.resolve(source)) as Observable<MessageEvent>;\n}","typeGuard":"import { isObservable, Observable } from 'rxjs';\n\nconst isSseStream = (v: unknown): v is Observable<{ data: unknown }> =>\n  isObservable(v);","tryCatchPattern":null,"preventionTips":["Declare every @Sse() handler as returning Observable<MessageEvent> so the compiler rejects async/Promise returns.","Never mark @Sse() methods async; stream pushes belong in RxJS operators, not awaited calls.","Bridge emitters/queues with fromEvent/webSocketSubject instead of polling promises."],"tags":["sse","rxjs","observable","routing","realtime"],"backgroundTag":"sse-invalid-return-type","analyzedSha":"dd75d7bd8c5e88048587e6768d36eb695f3e7a25","analyzedAt":"2026-08-21T19:39:39.867Z","contentChangedAt":"2026-08-21T19:39:39.867Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}