{"record":{"id":"fb0ee415d7436d98","repo":"benbjohnson/litestream","slug":"webdav-cannot-seek-temp-file-w","errorCode":null,"errorMessage":"webdav: cannot seek temp file: %w","messagePattern":"webdav: cannot seek temp file: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"webdav/replica_client.go","lineNumber":274,"sourceCode":"\t// on common configurations (Nginx+FastCGI, Lighttpd, Apache+FastCGI).\n\ttmpFile, err := os.CreateTemp(\"\", \"litestream-webdav-*.ltx\")\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"webdav: cannot create temp file: %w\", err)\n\t}\n\tdefer func() {\n\t\t_ = tmpFile.Close()\n\t\t_ = os.Remove(tmpFile.Name())\n\t}()\n\n\tfullReader := io.MultiReader(&buf, rd)\n\n\tsize, err := io.Copy(tmpFile, fullReader)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"webdav: cannot copy to temp file: %w\", err)\n\t}\n\n\tif _, err := tmpFile.Seek(0, io.SeekStart); err != nil {\n\t\treturn nil, fmt.Errorf(\"webdav: cannot seek temp file: %w\", err)\n\t}\n\n\tif err := client.MkdirAll(path.Dir(filename), 0755); err != nil {\n\t\treturn nil, fmt.Errorf(\"webdav: cannot create parent directory %q: %w\", path.Dir(filename), err)\n\t}\n\n\t// Upload with Content-Length header using seekable temp file.\n\t// WriteStreamWithLength requires both a seekable reader and known size,\n\t// which we now have from the temp file. This avoids chunked encoding\n\t// and ensures reliable uploads across all WebDAV server configurations.\n\tif err := client.WriteStreamWithLength(filename, tmpFile, size, 0644); err != nil {\n\t\treturn nil, fmt.Errorf(\"webdav: cannot write file %q: %w\", filename, err)\n\t}\n\n\tinternal.OperationTotalCounterVec.WithLabelValues(ReplicaClientType, \"PUT\").Inc()\n\tinternal.OperationBytesCounterVec.WithLabelValues(ReplicaClientType, \"PUT\").Add(float64(size))\n\n\treturn &ltx.FileInfo{","sourceCodeStart":256,"sourceCodeEnd":292,"githubUrl":"https://github.com/benbjohnson/litestream/blob/4ed7a308f6271ebfd2b0a6e4b70b03011a37e4a3/webdav/replica_client.go#L256-L292","documentation":"Before uploading, the staged temp file must be rewound to the start so its full contents can be sent. This error wraps a tmpFile.Seek failure, which is nearly always an OS-level I/O problem rather than a logical one.","triggerScenarios":"os.File.Seek on the just-written temp file fails, typically due to underlying filesystem errors (rare; e.g. I/O errors on the device, or the file descriptor became invalid).","commonSituations":"Failing disk/hardware I/O errors, exotic filesystems (some network mounts) with unreliable seek behavior, fd exhaustion side effects.","solutions":["Check dmesg/system logs for disk I/O errors on the temp filesystem","Verify the temp directory is on a local filesystem (avoid FUSE/network mounts for TMPDIR)","Retry the operation; transient I/O errors may clear","Check file descriptor limits if many concurrent writes are running"],"exampleFix":null,"handlingStrategy":"retry","validationCode":"probe, err := os.CreateTemp(os.TempDir(), \"seek-probe-*\")\nif err == nil { _, serr := probe.Seek(0, io.SeekStart); probe.Close(); os.Remove(probe.Name()); _ = serr }","typeGuard":null,"tryCatchPattern":"err := replicate(ctx)\nif err != nil && errors.Is(err, syscall.EIO) { /* check hardware/fs health, then retry */ }","preventionTips":["Keep TMPDIR on local filesystems, not FUSE/network mounts","Watch dmesg for disk I/O errors","Avoid fd leaks that can corrupt descriptor state"],"tags":["filesystem","seek","io"],"backgroundTag":"file-write-failed","analyzedSha":"4ed7a308f6271ebfd2b0a6e4b70b03011a37e4a3","analyzedAt":"2026-09-06T18:29:25.564Z","contentChangedAt":"2026-09-06T18:29:25.564Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}