{"record":{"id":"ead3b617ccde56f4","repo":"benbjohnson/litestream","slug":"webdav-cannot-copy-to-temp-file-w","errorCode":null,"errorMessage":"webdav: cannot copy to temp file: %w","messagePattern":"webdav: cannot copy to temp file: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"webdav/replica_client.go","lineNumber":270,"sourceCode":"\n\t// Stage to temporary file to get seekable reader with known size.\n\t// This ensures compatibility with all WebDAV servers and avoids the\n\t// unreliable chunked transfer encoding that causes silent data loss\n\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","sourceCodeStart":252,"sourceCodeEnd":288,"githubUrl":"https://github.com/benbjohnson/litestream/blob/4ed7a308f6271ebfd2b0a6e4b70b03011a37e4a3/webdav/replica_client.go#L252-L288","documentation":"After buffering the header bytes, WriteLTXFile copies the full LTX stream (header buffer + remainder) to the temp file with io.Copy. This error wraps any read error from the source stream or write error to the temp file during that copy.","triggerScenarios":"The source reader (rd) errors mid-stream (underlying DB/WAL read failure, closed pipe, network reader drop) or the temp file write fails (disk full, I/O error).","commonSituations":"Disk filling up during a large LTX write, the database connection producing the stream dropping mid-transfer, ENOSPC on the temp partition.","solutions":["Check disk space on the temp partition; free space or relocate TMPDIR","Investigate the source reader's error (the wrapped %w reveals the root cause)","Retry the replication write; transient stream failures are typically recoverable","Monitor for oversized LTX files exceeding available temp space"],"exampleFix":null,"handlingStrategy":"retry","validationCode":"if st, err := os.Stat(os.TempDir()); err != nil || st.Sys().(*syscall.Statfs_t).Bavail*uint64(st.Sys().(*syscall.Statfs_t).Bsize) < minFreeBytes { return errors.New(\"insufficient temp space\") }","typeGuard":null,"tryCatchPattern":"err := replicate(ctx)\nif err != nil {\n    if errors.Is(err, syscall.ENOSPC) { /* free space / alert */ } else if isTransient(err) { retry with backoff }\n}","preventionTips":["Alert on low disk space before it fills","Monitor stream-producing connections for drops","Size temp space for the largest expected LTX file"],"tags":["filesystem","io","stream"],"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"}