{"record":{"id":"47d6b6e5c5b669f3","repo":"hashicorp/nomad","slug":"host-volume-updates-already-batched","errorCode":null,"errorMessage":"host volume updates already batched","messagePattern":"host volume updates already batched","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"client/node_updater.go","lineNumber":408,"sourceCode":"\n// this is the one that the volume manager runs\nfunc (b *batchNodeUpdates) updateNodeFromHostVolume(name string, vol *structs.ClientHostVolumeConfig) {\n\tb.hostVolumeMu.Lock()\n\tdefer b.hostVolumeMu.Unlock()\n\tif b.hostVolumesBatched {\n\t\tb.hostVolumeCB(name, vol) // => Client.updateNodeFromHostVol()\n\t\treturn\n\t}\n\thvm.UpdateVolumeMap(b.logger.Named(\"node_updater\").With(\"method\", \"updateNodeFromHostVolume\"),\n\t\tb.hostVolumes, name, vol)\n}\n\n// this one runs on client start\nfunc (b *batchNodeUpdates) batchHostVolumeUpdates(f hvm.HostVolumeNodeUpdater) error {\n\tb.hostVolumeMu.Lock()\n\tdefer b.hostVolumeMu.Unlock()\n\tif b.hostVolumesBatched {\n\t\treturn fmt.Errorf(\"host volume updates already batched\")\n\t}\n\tb.hostVolumesBatched = true\n\tfor name, vol := range b.hostVolumes {\n\t\tf(name, vol) // => c.batchNodeUpdates.batchHostVolumeUpdates(FUNC\n\t}\n\treturn nil\n}\n\n// updateNodeFromCSI implements csimanager.UpdateNodeCSIInfoFunc and is used in\n// the csi manager to send csi fingerprints to the server.\nfunc (b *batchNodeUpdates) updateNodeFromCSI(plugin string, info *structs.CSIInfo) {\n\tb.csiMu.Lock()\n\tdefer b.csiMu.Unlock()\n\tif b.csiBatched {\n\t\tb.csiCB(plugin, info)\n\t\treturn\n\t}\n","sourceCodeStart":390,"sourceCodeEnd":426,"githubUrl":"https://github.com/hashicorp/nomad/blob/482b49bf1aec006f089bcfc7e632d8f6ac303e5e/client/node_updater.go#L390-L426","documentation":"batchHostVolumeUpdates registers a callback that flushes all batched host-volume node updates exactly once, guarded by hostVolumesBatched under a mutex. This error means the method was called a second time on the same batchNodeUpdates, which is a programming mistake: fingerprint batchers are one-shot per client startup.","triggerScenarios":"Calling batchNodeUpdates.batchHostVolumeUpdates more than once for the same client, e.g. invoking batchFirstFingerprints twice, or calling both batchFirstFingerprints and a manual batchHostVolumeUpdates on the same batcher instance.","commonSituations":"Refactoring client startup so first-fingerprint batching is triggered on reconnect in addition to initial start; accidental duplicate initialization of the node updater; test code reusing a batchNodeUpdates across runs without recreating it.","solutions":["Ensure batchHostVolumeUpdates is invoked only once per batchNodeUpdates instance, at client start (inside batchFirstFingerprints)","Recreate the batchNodeUpdates object for each fingerprint round that needs batching instead of reusing it","Guard call sites with a check of b.hostVolumesBatched, or log instead of returning an error if re-invocation is expected to be a no-op"],"exampleFix":"// before: reusing one batcher for repeated fingerprint rounds\nb := newBatchNodeUpdates()\nb.batchFirstFingerprints()\nb.batchFirstFingerprints() // -> \"host volume updates already batched\"\n\n// after: fresh batcher per round\nb := newBatchNodeUpdates()\nb.batchFirstFingerprints()\n\nb = newBatchNodeUpdates()\nb.batchFirstFingerprints()","handlingStrategy":"try-catch","validationCode":"if b.hostVolumesBatched {\n\t// skip duplicate batch registration entirely\n\treturn nil\n}","typeGuard":null,"tryCatchPattern":"if err := b.batchHostVolumeUpdates(fn); err != nil {\n\tif err.Error() == \"host volume updates already batched\" {\n\t\tlogger.Debug(\"host volume batch already registered; ignoring\")\n\t\treturn nil\n\t}\n\treturn err\n}","preventionTips":["Treat batchNodeUpdates as strictly one-shot per client start","Always create a fresh batchNodeUpdates on reconnect/restart paths","Centralize the batchFirstFingerprints call in a single startup code path","Assert single invocation in tests with a wrapper that panics on second call"],"tags":["nomad-client","fingerprinting","one-shot","state-machine"],"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"}