{"record":{"id":"4f344fd135838406","repo":"nsqio/nsq","slug":"exiting","errorCode":null,"errorMessage":"exiting","messagePattern":"exiting","errorType":"exception","errorClass":"FatalClientErr","httpStatus":503,"severity":"warning","filePath":"nsqd/topic.go","lineNumber":187,"sourceCode":"\t// update messagePump state\n\tselect {\n\tcase t.channelUpdateChan <- 1:\n\tcase <-t.exitChan:\n\t}\n\n\tif numChannels == 0 && t.ephemeral {\n\t\tgo t.deleter.Do(func() { t.deleteCallback(t) })\n\t}\n\n\treturn nil\n}\n\n// PutMessage writes a Message to the queue\nfunc (t *Topic) PutMessage(m *Message) error {\n\tt.RLock()\n\tdefer t.RUnlock()\n\tif atomic.LoadInt32(&t.exitFlag) == 1 {\n\t\treturn errors.New(\"exiting\")\n\t}\n\terr := t.put(m)\n\tif err != nil {\n\t\treturn err\n\t}\n\tatomic.AddUint64(&t.messageCount, 1)\n\tatomic.AddUint64(&t.messageBytes, uint64(len(m.Body)))\n\treturn nil\n}\n\n// PutMessages writes multiple Messages to the queue\nfunc (t *Topic) PutMessages(msgs []*Message) error {\n\tt.RLock()\n\tdefer t.RUnlock()\n\tif atomic.LoadInt32(&t.exitFlag) == 1 {\n\t\treturn errors.New(\"exiting\")\n\t}\n","sourceCodeStart":169,"sourceCodeEnd":205,"githubUrl":"https://github.com/nsqio/nsq/blob/85cf10c09c6c3c86160d6f0eb156f62d0efc1648/nsqd/topic.go#L169-L205","documentation":"Topic.PutMessage (nsqd/topic.go) takes a read lock and first checks the topic's exitFlag, an atomically-loaded int32 set when the topic is being deleted or nsqd is shutting down. If the flag is set, the write is refused with a plain 'exiting' error and the message is never enqueued; per-topic counters are not incremented. It is a lifecycle guard, not data corruption — the topic simply refuses new writes while it terminates.","triggerScenarios":"A producer publishes (PUB via the topic, HTTP /pub, or internal PutMessage from another component) concurrently with topic deletion — including automatic deletion of ephemeral topics when the last channel disappears (t.deleteCallback fires via the deleter) — or while nsqd is in Exit()/main shutdown, which sets exitFlag on every topic before flushing. HTTP producers get the raw error; TCP producers see it surfaced through the V2 protocol error path.","commonSituations":"Load tests that stop nsqd while producers are still connected; ephemeral topics (e.g. nsq_to_nsq or ephemeral channels with #ephemeral suffix) whose channels all disconnect while a producer keeps publishing; scripts publishing during topic deletion via the HTTP /topic/delete endpoint; graceful-shutdown races in CI.","solutions":["Treat this as transient: stop publishing to the topic, let deletion/shutdown finish, and recreate/re-resolve the topic before retrying.","For ephemeral topics, ensure at least one durable subscriber channel exists (do not let all channels be ephemeral) if you publish continuously.","In producers using go-nsq, rely on the Producer's built-in reconnect: it re-establishes and retries after the daemon restarts.","Sequence shutdown correctly: quiesce producers (or use a health gate) before sending SIGTERM to nsqd if you need loss-free in-flight writes."],"exampleFix":"// before: ignoring the error during shutdown/deletion races\n_ = topic.PutMessage(msg)\n\n// after: check the lifecycle error and re-resolve the topic\nif err := topic.PutMessage(msg); err != nil {\n    if err.Error() == \"exiting\" {\n        topic = nsqd.GetTopic(topicName) // topic was deleted/restarting; retry once\n        err = topic.PutMessage(msg)\n    }\n    if err != nil {\n        return err\n    }\n}","handlingStrategy":"retry","validationCode":"// producer-side gate before publishing during deploys\nif !nsqdHealthy(addr) { // e.g. GET /info via HTTP health port\n    waitOrPanic()\n}","typeGuard":null,"tryCatchPattern":"// go-nsq Producer already maps connection-level failures to retries; for direct\n// topic API use, treat \"exiting\" as retriable:\nerr := topic.PutMessage(m)\nif err != nil && err.Error() == \"exiting\" {\n    topic = getOrCreateTopic(name) // re-resolve after delete/shutdown race\n    err = topic.PutMessage(m)\n}","preventionTips":["Quiesce producers before stopping/deleting nsqd or topics in runbooks.","Avoid publishing to ephemeral topics unless a durable channel exists.","Use go-nsq Producer, which reconnects and retries across nsqd restarts."],"tags":["lifecycle","shutdown","topic","concurrency","nsqd"],"backgroundTag":null,"analyzedSha":"85cf10c09c6c3c86160d6f0eb156f62d0efc1648","analyzedAt":"2026-08-16T00:53:05.009Z","schemaVersion":2},"datasetVersion":"2026-08-16T03:17:38.424Z"}