{"record":{"id":"6ae0395669afe58e","repo":"hashicorp/nomad","slug":"csi-updates-already-batched","errorCode":null,"errorMessage":"csi updates already batched","messagePattern":"csi updates already batched","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"client/node_updater.go","lineNumber":444,"sourceCode":"\n\t// Only one of these is expected to be set, but a future implementation that\n\t// explicitly models monolith plugins with a single fingerprinter may set both\n\tif info.ControllerInfo != nil {\n\t\tb.csiControllerPlugins[plugin] = info\n\t}\n\n\tif info.NodeInfo != nil {\n\t\tb.csiNodePlugins[plugin] = info\n\t}\n}\n\n// batchCSIUpdates sends all of the batched CSI updates by calling f  for each\n// plugin batched\nfunc (b *batchNodeUpdates) batchCSIUpdates(f csimanager.UpdateNodeCSIInfoFunc) error {\n\tb.csiMu.Lock()\n\tdefer b.csiMu.Unlock()\n\tif b.csiBatched {\n\t\treturn fmt.Errorf(\"csi updates already batched\")\n\t}\n\n\tb.csiBatched = true\n\tfor plugin, info := range b.csiNodePlugins {\n\t\tf(plugin, info)\n\t}\n\tfor plugin, info := range b.csiControllerPlugins {\n\t\tf(plugin, info)\n\t}\n\treturn nil\n}\n\n// updateNodeFromDriver implements drivermanager.UpdateNodeDriverInfoFn and is\n// used in the driver manager to send driver fingerprints to\nfunc (b *batchNodeUpdates) updateNodeFromDriver(driver string, info *structs.DriverInfo) {\n\tb.driversMu.Lock()\n\tdefer b.driversMu.Unlock()\n\tif b.driversBatched {","sourceCodeStart":426,"sourceCodeEnd":462,"githubUrl":"https://github.com/hashicorp/nomad/blob/482b49bf1aec006f089bcfc7e632d8f6ac303e5e/client/node_updater.go#L426-L462","documentation":"batchCSIUpdates registers the one-shot callback that flushes batched CSI plugin node updates, guarded by csiBatched under csiMu. This error means the method was invoked a second time on the same batchNodeUpdates, which is only meant to run once during the first fingerprinting round at client startup.","triggerScenarios":"Calling batchNodeUpdates.batchCSIUpdates (directly or via batchFirstFingerprints) more than once on the same batcher instance, e.g. on client restart/reconnect while reusing the old batcher.","commonSituations":"Re-running the first-fingerprint batching path after a server reconnect; duplicate wiring of the CSI fingerprinter in client startup; unit tests invoking the batching functions twice on the same struct.","solutions":["Call batchCSIUpdates exactly once per batchNodeUpdates lifetime, from batchFirstFingerprints during client start","Create a new batchNodeUpdates instance for each subsequent batching round","If re-invocation is legitimate in your flow, tolerate or log this sentinel error instead of failing startup"],"exampleFix":"// before: same batcher reused after reconnect\nif err := b.batchCSIUpdates(fn); err != nil { /* already batched */ }\n\n// after: reset or recreate batcher on reconnect\nb = newBatchNodeUpdates()\nif err := b.batchCSIUpdates(fn); err != nil {\n    return err\n}","handlingStrategy":"try-catch","validationCode":"if b.csiBatched {\n\treturn nil // already registered; avoid the sentinel error\n}","typeGuard":null,"tryCatchPattern":"if err := b.batchCSIUpdates(fn); err != nil {\n\tif err.Error() == \"csi updates already batched\" {\n\t\tlogger.Debug(\"csi batch already registered; ignoring\")\n\t\treturn nil\n\t}\n\treturn err\n}","preventionTips":["Invoke batchCSIUpdates only from the single batchFirstFingerprints path","Recreate the batcher for each fingerprint round","Don't reuse startup batchers across reconnects","Add startup tracing to confirm batching runs exactly once"],"tags":["nomad-client","csi","fingerprinting","one-shot"],"backgroundTag":"already-initialized","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"}