{"record":{"id":"e1f9e4823ed26b67","repo":"hashicorp/nomad","slug":"error-stream-is-no-longer-running-w","errorCode":null,"errorMessage":"error stream is no longer running: %w","messagePattern":"error stream is no longer running: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"nomad/stream/ndjson.go","lineNumber":90,"sourceCode":"}\n\n// Send encodes an object into Newline delimited json. An error is returned\n// if json encoding fails or if the stream is no longer running.\nfunc (n *JsonStream) Send(v any) error {\n\tif n.ctx.Err() != nil {\n\t\treturn n.ctx.Err()\n\t}\n\n\tvar buf bytes.Buffer\n\tenc := codec.NewEncoder(&buf, structs.JsonHandleWithExtensions)\n\terr := enc.Encode(v)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"error marshaling json for stream: %w\", err)\n\t}\n\n\tselect {\n\tcase <-n.ctx.Done():\n\t\treturn fmt.Errorf(\"error stream is no longer running: %w\", err)\n\tcase n.outCh <- &structs.EventJson{Data: buf.Bytes()}:\n\t}\n\n\treturn nil\n}\n","sourceCodeStart":72,"sourceCodeEnd":96,"githubUrl":"https://github.com/hashicorp/nomad/blob/482b49bf1aec006f089bcfc7e632d8f6ac303e5e/nomad/stream/ndjson.go#L72-L96","documentation":"This error is returned by the NDJSON stream's Send method in nomad/stream/ndjson.go when the stream's context has been cancelled while trying to publish an event. It wraps the underlying send error and signals that consumers can no longer receive events because the stream pipeline is shut down. It exists so callers draining a subscription learn immediately that the output channel is dead rather than blocking forever on a send.","triggerScenarios":"Calling Send on a JsonStream after its context (n.ctx) was cancelled — e.g. the HTTP request ended, the client disconnected, Stop() was called, or the subscription was torn down — while the internal outCh buffer path also failed, so the send falls through to the ctx.Done() branch. Notably it wraps `err` from the preceding json.Marshal, so when marshaling fails on a stopped stream this message appears with the marshal error as the cause.","commonSituations":"A WebSocket/HTTP streaming client disconnects mid-stream and the handler keeps calling Send for events that arrive afterwards (e.g. exec task streaming, event streams in `nomad monitor`). Also seen in tests like TestJson_Send_After_Stop. A latent bug: the ctx.Done() branch wraps the marshaling `err`, which is nil in the plain cancel case, yielding the odd message 'error stream is no longer running: %!w(<nil>)'.","solutions":["Stop calling Send once the stream is stopped: check stream context cancellation (or the error from Send) and exit the producer loop.","Ensure the producer goroutine's lifetime is tied to the stream context — use errgroup or a done channel derived from the request context so sends stop when the client disconnects.","If this surfaces during exec streaming, verify the exec session is not being driven after the raw stream (ExecTaskStreamingRaw) has returned.","If the wrapped cause is a json.Marshal error on a stopped stream, fix the marshal failure too — that error indicates an event payload with unsupported types or an EncodeValue missing."],"exampleFix":"// before\nfor event := range events {\n    if err := stream.Send(event); err != nil {\n        return err\n    }\n}\n// after\nfor event := range events {\n    if err := stream.Send(event); err != nil {\n        if strings.Contains(err.Error(), \"error stream is no longer running\") {\n            return nil // client disconnected; stop producer cleanly\n        }\n        return err\n    }\n}","handlingStrategy":"try-catch","validationCode":"select {\ncase <-streamCtx.Done():\n    // skip send; stream already stopped\n    return nil\ndefault:\n}\n// proceed to stream.Send(event)","typeGuard":"func streamAlive(ctx context.Context) bool {\n    select {\n    case <-ctx.Done():\n        return false\n    default:\n        return true\n    }\n}","tryCatchPattern":"if err := stream.Send(event); err != nil {\n    if strings.Contains(err.Error(), \"error stream is no longer running\") {\n        return nil // graceful shutdown\n    }\n    return fmt.Errorf(\"stream send failed: %w\", err)\n}","preventionTips":["Tie the event producer goroutine to the stream's context so sends stop on cancellation.","Treat any Send error as terminal and exit the loop; never retry sends on a stopped stream.","Marshal payloads before checking/inside Send flows so marshal failures are reported distinctly.","In tests, stop the stream only after all Send calls complete (errgroup-style teardown)."],"tags":["streaming","context-cancelled","ndjson","goroutine-lifecycle"],"backgroundTag":"stream-context-cancelled","analyzedSha":"482b49bf1aec006f089bcfc7e632d8f6ac303e5e","analyzedAt":"2026-09-04T07:54:14.808Z","contentChangedAt":"2026-09-04T07:54:14.808Z","schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}