{"record":{"id":"59d0278811f72305","repo":"hashicorp/nomad","slug":"error-reading-snapshot-w","errorCode":null,"errorMessage":"error reading snapshot: %w","messagePattern":"error reading snapshot: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"client/allocwatcher/alloc_watcher.go","lineNumber":684,"sourceCode":"\t\t\t\t\treturn fmt.Errorf(\"error chowning file %w\", err)\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// We write in chunks so that we can test if the client\n\t\t\t// is still alive\n\t\t\tfor !canceled() {\n\t\t\t\tn, err := tr.Read(buf)\n\t\t\t\tif n > 0 && (err == nil || err == io.EOF) {\n\t\t\t\t\tif _, err := f.Write(buf[:n]); err != nil {\n\t\t\t\t\t\tf.Close()\n\t\t\t\t\t\treturn fmt.Errorf(\"error writing to file %q: %w\", f.Name(), err)\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tif err != nil {\n\t\t\t\t\tf.Close()\n\t\t\t\t\tif err != io.EOF {\n\t\t\t\t\t\treturn fmt.Errorf(\"error reading snapshot: %w\", err)\n\t\t\t\t\t}\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t}\n\n\t\t}\n\t}\n\n\tif canceled() {\n\t\treturn ctx.Err()\n\t}\n\n\treturn nil\n}\n\n// NoopPrevAlloc does not block or migrate on a previous allocation and never\n// returns an error.\ntype NoopPrevAlloc struct{}","sourceCodeStart":666,"sourceCodeEnd":702,"githubUrl":"https://github.com/hashicorp/nomad/blob/482b49bf1aec006f089bcfc7e632d8f6ac303e5e/client/allocwatcher/alloc_watcher.go#L666-L702","documentation":"streamAllocDir reads a tar-formatted snapshot of the previous allocation's directory over a pipe/tar reader. If the tar read returns a non-EOF error, Nomad aborts the extraction and wraps it as \"error reading snapshot\".","triggerScenarios":"The tar stream produced by the previous allocation's alloc dir reader (tar.Reader.Next / tr.Read) returns a non-io.EOF error — the stream was truncated, the underlying pipe/reader was closed prematurely, or the archive is corrupt.","commonSituations":"Previous allocation's client shut down while its alloc dir was still streaming; corrupted state on disk; network/pipe interruption between the migrating alloc reader and this goroutine.","solutions":["Retry the allocation migration; the framework will reschedule if migration fails.","Check the source alloc dir for corruption or missing files on the old client.","Look at the wrapped inner error (e.g. unexpected EOF, pipe broken) and check client logs around the migration window.","Upgrade Nomad if this recurs during node drain/migration — several tar/stream handling bugs were fixed over time."],"exampleFix":"// no code fix; diagnose wrapped cause:\n// error reading snapshot: archive/tar: unexpected EOF\n// => old client's alloc dir changed mid-stream; retry migration on a stable node","handlingStrategy":"retry","validationCode":"// precheck: verify the previous alloc's dir is readable and non-empty before migrating\nif st, err := os.Stat(prevAllocDir); err != nil || !st.IsDir() {\n    return fmt.Errorf(\"prev alloc dir unreadable: %w\", err)\n}","typeGuard":null,"tryCatchPattern":"// wrap migration and retry on snapshot errors\nerr := migrateAllocDir(prevAlloc)\nif err != nil && strings.Contains(err.Error(), \"error reading snapshot\") {\n    // transient stream interruption; let the scheduler reschedule the alloc\n    logger.Warn(\"snapshot read failed, waiting for reschedule\", \"err\", err)\n}","preventionTips":["Avoid draining or killing a client while its allocs are being migrated","Keep state disks healthy — corruption mid-stream causes tar read errors","Retry failed migrations; they are typically transient","Check client logs for the wrapped tar error to distinguish truncation vs corruption"],"tags":["io","tar","alloc-migration"],"backgroundTag":"archive-read-failed","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"}