{"record":{"id":"d1b8830218fc0699","repo":"hashicorp/nomad","slug":"can-t-seek-to-offset-d-w","errorCode":null,"errorMessage":"can't seek to offset %d: %w","messagePattern":"can't seek to offset (.+?): %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"client/allocdir/alloc_dir.go","lineNumber":510,"sourceCode":"\tif strings.HasSuffix(path, \".json\") {\n\t\tcontentType = \"application/json\"\n\t}\n\treturn contentType\n}\n\n// ReadAt returns a reader for a file at the path relative to the alloc dir\nfunc (a *AllocDir) ReadAt(path string, offset int64) (io.ReadCloser, error) {\n\tsanitizedPath, err := a.sanitizePath(path)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tf, err := os.Open(sanitizedPath)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tif _, err := f.Seek(offset, 0); err != nil {\n\t\treturn nil, fmt.Errorf(\"can't seek to offset %d: %w\", offset, err)\n\t}\n\treturn f, nil\n}\n\n// BlockUntilExists blocks until the passed file relative the allocation\n// directory exists. The block can be cancelled with the passed context.\nfunc (a *AllocDir) BlockUntilExists(ctx context.Context, path string) (chan error, error) {\n\tsanitizedPath, err := a.sanitizePath(path)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\t// Get the path relative to the alloc directory\n\twatcher := getFileWatcher(sanitizedPath)\n\treturnCh := make(chan error, 1)\n\tt := &tomb.Tomb{}\n\tgo func() {\n\t\t<-ctx.Done()","sourceCodeStart":492,"sourceCodeEnd":528,"githubUrl":"https://github.com/hashicorp/nomad/blob/482b49bf1aec006f089bcfc7e632d8f6ac303e5e/client/allocdir/alloc_dir.go#L492-L528","documentation":"AllocDir.ReadAt fails when os.Seek to the requested offset on the opened alloc-dir file returns an error (e.g. offset beyond file size or an I/O error); the underlying seek error is wrapped and returned to the caller of the file-read endpoint.","triggerScenarios":"Calling ReadAt with a negative offset, or an offset beyond what the filesystem/file supports (e.g. past the end on a special file such as a pipe, device, or FIFO that is not seekable).","commonSituations":"Log/EOF-following logic computing an offset from a previous snapshot of a file replaced by a special file; offset from stale state after file truncation on filesystems that reject out-of-range seeks; reading non-regular files (sockets/pipes) created inside the alloc dir.","solutions":["Check the offset is >= 0 and the target is a regular file before calling ReadAt","Handle the wrapped seek error by re-statting the file and restarting at offset 0","Avoid pointing the file API at non-seekable files (pipes, sockets, devices)","Refresh stale offsets after the file has been rotated or truncated"],"exampleFix":"// before\nf, err := allocDir.ReadAt(\"task/app/log\", staleOffset)\n// after\nfi, _ := os.Stat(\"task/app/log\")\noff := staleOffset\nif !fi.Mode().IsRegular() || off > fi.Size() {\n    off = 0\n}\nf, err := allocDir.ReadAt(\"task/app/log\", off)","handlingStrategy":"validation","validationCode":"fi, err := os.Stat(path)\nif err != nil || !fi.Mode().IsRegular() {\n    return fmt.Errorf(\"not a seekable regular file\")\n}\nif offset < 0 || offset > fi.Size() {\n    offset = 0\n}","typeGuard":null,"tryCatchPattern":"f, err := allocDir.ReadAt(relPath, off)\nif err != nil && strings.Contains(err.Error(), \"can't seek to offset\") {\n    f, err = allocDir.ReadAt(relPath, 0) // restart from beginning\n}","preventionTips":["Validate offsets come from a current stat, not stale state","Never point the file API at pipes/sockets/devices","Reset offset to 0 after file rotation/truncation"],"tags":["filesystem","io","seek"],"backgroundTag":"seek-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"}