{"record":{"id":"aabf7adb2b26decd","repo":"benbjohnson/litestream","slug":"webdav-cannot-read-file-q-w","errorCode":null,"errorMessage":"webdav: cannot read file %q: %w","messagePattern":"webdav: cannot read file %q: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"webdav/replica_client.go","lineNumber":317,"sourceCode":"}\n\nfunc (c *ReplicaClient) OpenLTXFile(ctx context.Context, level int, minTXID, maxTXID ltx.TXID, offset, size int64) (_ io.ReadCloser, err error) {\n\tclient, err := c.init(ctx)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tfilename := litestream.LTXFilePath(c.Path, level, minTXID, maxTXID)\n\n\tinternal.OperationTotalCounterVec.WithLabelValues(ReplicaClientType, \"GET\").Inc()\n\n\tif size > 0 {\n\t\trc, err := client.ReadStreamRange(filename, offset, size)\n\t\tif err != nil {\n\t\t\tif os.IsNotExist(err) || gowebdav.IsErrNotFound(err) {\n\t\t\t\treturn nil, os.ErrNotExist\n\t\t\t}\n\t\t\treturn nil, fmt.Errorf(\"webdav: cannot read file %q: %w\", filename, err)\n\t\t}\n\t\treturn internal.LimitReadCloser(rc, size), nil\n\t}\n\n\tif offset > 0 {\n\t\trc, err := client.ReadStream(filename)\n\t\tif err != nil {\n\t\t\tif os.IsNotExist(err) || gowebdav.IsErrNotFound(err) {\n\t\t\t\treturn nil, os.ErrNotExist\n\t\t\t}\n\t\t\treturn nil, fmt.Errorf(\"webdav: cannot read file %q: %w\", filename, err)\n\t\t}\n\n\t\tif _, err := io.CopyN(io.Discard, rc, offset); err != nil {\n\t\t\tif err == io.EOF || err == io.ErrUnexpectedEOF {\n\t\t\t\t_ = rc.Close()\n\t\t\t\treturn io.NopCloser(bytes.NewReader(nil)), nil\n\t\t\t}","sourceCodeStart":299,"sourceCodeEnd":335,"githubUrl":"https://github.com/benbjohnson/litestream/blob/4ed7a308f6271ebfd2b0a6e4b70b03011a37e4a3/webdav/replica_client.go#L299-L335","documentation":"OpenLTXFile with a known size uses client.ReadStreamRange to fetch only the requested byte range. If the range request fails and the object is not simply missing, the error is wrapped as 'cannot read file'. Note: os.ErrNotExist is returned unchanged when the server reports the file not found, so this error means a real read failure.","triggerScenarios":"ReadStreamRange (HTTP Range GET) returns a non-404 error: server not honoring Range headers, TLS failures, auth rejection, or connection errors during the ranged read.","commonSituations":"WebDAV server without Range support (litestream relies on it for partial reads), intermediate proxies stripping Range headers, expired sessions/token mid-restore.","solutions":["Confirm the server supports Range requests (check for Accept-Ranges in a HEAD response)","Check credentials and URL correctness in the litestream config","Retry the restore; transient network failures are often intermittent","Bypass proxies or configure them to pass Range headers through"],"exampleFix":null,"handlingStrategy":"retry","validationCode":"// verify server supports ranges before relying on partial reads\nresp, err := http.Head(fileURL)\nif err == nil && resp.Header.Get(\"Accept-Ranges\") != \"bytes\" { log.Warn(\"WebDAV server lacks Range support\") }","typeGuard":null,"tryCatchPattern":"rc, err := OpenLTXFile(ctx, info)\nif err != nil {\n    if errors.Is(err, os.ErrNotExist) { /* object genuinely missing */ }\n    else if isTransient(err) { retry with backoff }\n}","preventionTips":["Choose a WebDAV server with Range request support","Configure proxies to pass Range headers","Retry restores on transient network failures"],"tags":["webdav","range-request","read"],"backgroundTag":"http-request-failed","analyzedSha":"4ed7a308f6271ebfd2b0a6e4b70b03011a37e4a3","analyzedAt":"2026-09-06T18:29:25.564Z","contentChangedAt":"2026-09-06T18:29:25.564Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}