{"record":{"id":"34b77770c44333f7","repo":"nsqio/nsq","slug":"id-not-in-flight","errorCode":null,"errorMessage":"ID not in flight","messagePattern":"ID not in flight","errorType":"exception","errorClass":"ClientErr","httpStatus":null,"severity":"warning","filePath":"nsqd/channel.go","lineNumber":562,"sourceCode":"func (c *Channel) pushInFlightMessage(msg *Message) error {\n\tc.inFlightMutex.Lock()\n\t_, ok := c.inFlightMessages[msg.ID]\n\tif ok {\n\t\tc.inFlightMutex.Unlock()\n\t\treturn errors.New(\"ID already in flight\")\n\t}\n\tc.inFlightMessages[msg.ID] = msg\n\tc.inFlightMutex.Unlock()\n\treturn nil\n}\n\n// popInFlightMessage atomically removes a message from the in-flight dictionary\nfunc (c *Channel) popInFlightMessage(clientID int64, id MessageID) (*Message, error) {\n\tc.inFlightMutex.Lock()\n\tmsg, ok := c.inFlightMessages[id]\n\tif !ok {\n\t\tc.inFlightMutex.Unlock()\n\t\treturn nil, errors.New(\"ID not in flight\")\n\t}\n\tif msg.clientID != clientID {\n\t\tc.inFlightMutex.Unlock()\n\t\treturn nil, errors.New(\"client does not own message\")\n\t}\n\tdelete(c.inFlightMessages, id)\n\tc.inFlightMutex.Unlock()\n\treturn msg, nil\n}\n\nfunc (c *Channel) addToInFlightPQ(msg *Message) {\n\tc.inFlightMutex.Lock()\n\tc.inFlightPQ.Push(msg)\n\tc.inFlightMutex.Unlock()\n}\n\nfunc (c *Channel) removeFromInFlightPQ(msg *Message) {\n\tc.inFlightMutex.Lock()","sourceCodeStart":544,"sourceCodeEnd":580,"githubUrl":"https://github.com/nsqio/nsq/blob/85cf10c09c6c3c86160d6f0eb156f62d0efc1648/nsqd/channel.go#L544-L580","documentation":"popInFlightMessage is the single lookup path for FIN, REQ and TOUCH: it fetches the MessageID from the channel's inFlightMessages map under inFlightMutex and returns errors.New(\"ID not in flight\") when the ID is absent. The protocol layer wraps it as E_FIN_FAILED / E_REQ_FAILED / E_TOUCH_FAILED ('<CMD> <id> failed ID not in flight'). The ID is absent whenever it was already removed - a duplicate FIN/REQ/TOUCH, a completed timeout requeue, or channel teardown.","triggerScenarios":"Sending FIN for a message whose in-flight timeout already fired and requeued it; sending FIN twice for the same ID; sending TOUCH/REQ after an earlier FIN succeeded; sending any of these after DELETE /channel or /topic removed the channel state. Message-level races between client RDH/timeout and server requeue are the classic producer.","commonSituations":"Slow consumers whose msg_timeout expires while they are still processing: the server requeues the message and the later FIN finds nothing; at-least-once consumers that re-FIN during shutdown; duplicate command emission after client reconnects replay handler state.","solutions":["Treat 'ID not in flight' on FIN/TOUCH as benign idempotency fallout: log it at debug and continue (the message is already requeued and will be redelivered).","Raise --msg-timeout (or the client's per-message timeout) and TOUCH long-running messages before the deadline so ownership is not lost.","Never re-send FIN/REQ for an ID after an error or reconnect without expecting this response; make handlers idempotent per message ID.","During topology changes, stop processing before channels are deleted."],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"err := conn.FinishMessage(id) // or TouchMessage / RequeueMessage\nif err != nil {\n\tif strings.Contains(err.Error(), \"ID not in flight\") {\n\t\t// already FIN'd, REQ'd, timed out and requeued, or channel state was reset:\n\t\t// harmless duplicate ack - ignore, redelivery will happen if needed\n\t\treturn nil\n\t}\n\treturn err\n}","preventionTips":["Deduplicate FIN/REQ per message ID inside your handler wrapper (sync.Map of in-flight IDs) so commands are never sent twice.","TOUCH long-running messages before msg-timeout expires so ownership is not silently lost to the timeout requeue.","Log 'ID not in flight' at INFO with metrics - a rising rate means timeouts are too tight, not that data is lost."],"tags":["nsq","nsqd","protocol","concurrency","idempotency"],"backgroundTag":null,"analyzedSha":"85cf10c09c6c3c86160d6f0eb156f62d0efc1648","analyzedAt":"2026-08-16T00:53:05.009Z","schemaVersion":2},"datasetVersion":"2026-08-16T03:17:38.424Z"}