{"record":{"id":"5613513d2eb9e0f7","repo":"AlistGo/alist","slug":"send-failed","errorCode":null,"errorMessage":"send failed","messagePattern":"send failed","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"internal/message/http.go","lineNumber":48,"sourceCode":"\tvar req Req\n\tif err := c.ShouldBind(&req); err != nil {\n\t\tcommon.ErrorResp(c, err, 400)\n\t\treturn\n\t}\n\tselect {\n\tcase p.Received <- req.Message:\n\t\tcommon.SuccessResp(c)\n\tdefault:\n\t\tcommon.ErrorStrResp(c, \"nowhere needed\", 500)\n\t}\n}\n\nfunc (p *Http) Send(message Message) error {\n\tselect {\n\tcase p.ToSend <- message:\n\t\treturn nil\n\tdefault:\n\t\treturn errors.New(\"send failed\")\n\t}\n}\n\nfunc (p *Http) Receive() (string, error) {\n\tselect {\n\tcase message := <-p.Received:\n\t\treturn message, nil\n\tdefault:\n\t\treturn \"\", errors.New(\"receive failed\")\n\t}\n}\n\nfunc (p *Http) WaitSend(message Message, d int) error {\n\tselect {\n\tcase p.ToSend <- message:\n\t\treturn nil\n\tcase <-time.After(time.Duration(d) * time.Second):\n\t\treturn errors.New(\"send timeout\")","sourceCodeStart":30,"sourceCodeEnd":66,"githubUrl":"https://github.com/AlistGo/alist/blob/843d9dc8149126976b2625911e45a4d3ffd6f2f5/internal/message/http.go#L30-L66","documentation":"'send failed' is returned by (*Http).Send in internal/message/http.go when a non-blocking send on the unbuffered ToSend channel cannot proceed — i.e. no receiver is currently blocked reading from HttpInstance.ToSend. The select's default branch fires instead of blocking the caller.","triggerScenarios":"Calling HttpInstance.Send(msg) at a moment when no HTTP polling consumer is waiting in the receive branch that drains ToSend (the long-poll handler in the same file). Because the channel is unbuffered, a send only succeeds if a receiver is already parked on it.","commonSituations":"Frontend/browser tab not open or its message long-poll disconnected, so server-side code pushing a notification gets an immediate error; timing races where the message is produced just before the poll request arrives; multiple producers pushing while the single poller is between requests.","solutions":["Use WaitSend(message, d) instead of Send when the message may be produced before a poller is attached — it blocks up to d seconds for a receiver","Ensure a consumer (the message-polling HTTP endpoint) is connected before producing messages","Treat this error as 'message dropped, no consumer' and log/skip rather than retrying blindly","If message delivery matters, switch to a buffered channel or a queue-backed mechanism"],"exampleFix":"// before\nerr := message.HttpInstance.Send(msg)\n\n// after\nerr := message.HttpInstance.WaitSend(msg, 5) // wait up to 5s for a poller","handlingStrategy":"fallback","validationCode":"// establish a poller before producing messages\n// (client keeps the message long-poll active; server side:) go drainLoop()","typeGuard":"func isSendFailed(err error) bool {\n    return err != nil && err.Error() == \"send failed\"\n}","tryCatchPattern":"if err := message.HttpInstance.Send(msg); isSendFailed(err) {\n    // no consumer attached: drop or enqueue; never crash\n}","preventionTips":["Prefer WaitSend with a bounded timeout over Send","Keep the browser message poll connected before triggering notifications","Treat this error as 'no consumer', not a fault","Log at debug level to avoid noise"],"tags":["channel","messaging","concurrency"],"backgroundTag":null,"analyzedSha":"843d9dc8149126976b2625911e45a4d3ffd6f2f5","analyzedAt":"2026-08-15T12:14:11.722Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}