{"record":{"id":"d40fa3907205b4ee","repo":"juicedata/juicefs","slug":"capture-read-position-s-w","errorCode":null,"errorMessage":"capture read position %s: %w","messagePattern":"capture read position (.+?): %w","errorType":"error_code","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/object/storj.go","lineNumber":151,"sourceCode":"\t\tdownload, e = s.project.DownloadObject(ctx, s.bucket, key, opts)\n\t\treturn e\n\t})\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn download, nil\n}\n\nfunc (s *storjClient) Put(ctx context.Context, key string, in io.Reader, getters ...AttrGetter) error {\n\t// Retry is only safe when the reader is seekable: rewinding lets us restart\n\t// the upload from the beginning after a rate-limit response.\n\trs, seekable := in.(io.ReadSeeker)\n\tif !seekable {\n\t\treturn s.putOnce(ctx, key, in)\n\t}\n\tstartPos, err := rs.Seek(0, io.SeekCurrent)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"capture read position %s: %w\", key, err)\n\t}\n\treturn storjBackoff(ctx, func() error {\n\t\tif _, err := rs.Seek(startPos, io.SeekStart); err != nil {\n\t\t\treturn err\n\t\t}\n\t\treturn s.putOnce(ctx, key, rs)\n\t})\n}\n\nfunc (s *storjClient) putOnce(ctx context.Context, key string, in io.Reader) error {\n\tupload, err := s.project.UploadObject(ctx, s.bucket, key, nil)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"begin upload %s: %w\", key, err)\n\t}\n\tif _, err = io.Copy(upload, in); err != nil {\n\t\t_ = upload.Abort()\n\t\treturn fmt.Errorf(\"upload %s: %w\", key, err)\n\t}","sourceCodeStart":133,"sourceCodeEnd":169,"githubUrl":"https://github.com/juicedata/juicefs/blob/c9a67b23e8e08ec23ec331aa6f1675e2319e921c/pkg/object/storj.go#L133-L169","documentation":"storjClient.Put first checks whether the input is an io.ReadSeeker so it can retry uploads by rewinding. Capturing the current position with rs.Seek(0, io.SeekCurrent) failing means the reader advertised seekability but the underlying Seek operation failed, so a resumable/retried upload cannot be safely started.","triggerScenarios":"Passing a reader that implements io.ReadSeeker but whose Seek returns an error — e.g. an *os.File on a pipe/non-seekable fd, a closed file, or a custom reader whose Seek is unimplemented/fails at runtime, then calling Put.","commonSituations":"Piping from stdin or a process substitution into a JuiceFS command whose upload path expects seekable input; a wrapped reader (bufio/compression) that falsely claims ReadSeeker support.","solutions":["Supply a fully seekable source (real file or bytes.Reader) instead of a pipe/stdin.","If the input is not seekable, ensure it reaches the path that uses putOnce (single-shot upload) — don't wrap it in a fake ReadSeeker.","If using a custom reader, implement Seek correctly or remove the io.ReadSeeker interface so Put falls back to putOnce."],"exampleFix":"// before\nin := bufio.NewReader(os.Stdin) // not truly seekable\nclient.Put(ctx, key, in)\n// after\nin, err := os.Open(tmpFile)\nif err != nil { return err }\ndefer in.Close()\nclient.Put(ctx, key, in) // real io.ReadSeeker","handlingStrategy":"type-guard","validationCode":"if rs, ok := in.(io.ReadSeeker); ok {\n    if _, err := rs.Seek(0, io.SeekCurrent); err != nil {\n        return fmt.Errorf(\"input not truly seekable: %v\", err)\n    }\n}","typeGuard":"func isSeekable(r io.Reader) bool {\n    rs, ok := r.(io.ReadSeeker)\n    if !ok { return false }\n    _, err := rs.Seek(0, io.SeekCurrent)\n    return err == nil\n}","tryCatchPattern":"if err := client.Put(ctx, key, in); err != nil && strings.Contains(err.Error(), \"capture read position\") {\n    // fall back to staging the data into a temp file, then retry Put\n}","preventionTips":["Feed Put real files or in-memory buffers, not pipes/stdin.","Don't wrap non-seekable readers in types that claim io.ReadSeeker.","Test upload paths with piped input in CI to catch false seekability."],"tags":["storj","upload","io"],"backgroundTag":"unsupported-operation","analyzedSha":"c9a67b23e8e08ec23ec331aa6f1675e2319e921c","analyzedAt":"2026-09-06T17:55:48.476Z","contentChangedAt":"2026-09-06T17:55:48.476Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}