benbjohnson/litestream · error
webdav: cannot create parent directory %q: %w
Error message
webdav: cannot create parent directory %q: %w
What it means
WriteLTXFile ensures the destination directory exists on the WebDAV server via client.MkdirAll before uploading. This error wraps that failure: the server rejected directory creation (permissions, locked collection, or HTTP-level problem).
Source
Thrown at webdav/replica_client.go:278
}
defer func() {
_ = tmpFile.Close()
_ = os.Remove(tmpFile.Name())
}()
fullReader := io.MultiReader(&buf, rd)
size, err := io.Copy(tmpFile, fullReader)
if err != nil {
return nil, fmt.Errorf("webdav: cannot copy to temp file: %w", err)
}
if _, err := tmpFile.Seek(0, io.SeekStart); err != nil {
return nil, fmt.Errorf("webdav: cannot seek temp file: %w", err)
}
if err := client.MkdirAll(path.Dir(filename), 0755); err != nil {
return nil, fmt.Errorf("webdav: cannot create parent directory %q: %w", path.Dir(filename), err)
}
// Upload with Content-Length header using seekable temp file.
// WriteStreamWithLength requires both a seekable reader and known size,
// which we now have from the temp file. This avoids chunked encoding
// and ensures reliable uploads across all WebDAV server configurations.
if err := client.WriteStreamWithLength(filename, tmpFile, size, 0644); err != nil {
return nil, fmt.Errorf("webdav: cannot write file %q: %w", filename, err)
}
internal.OperationTotalCounterVec.WithLabelValues(ReplicaClientType, "PUT").Inc()
internal.OperationBytesCounterVec.WithLabelValues(ReplicaClientType, "PUT").Add(float64(size))
return <x.FileInfo{
Level: level,
MinTXID: minTXID,
MaxTXID: maxTXID,
Size: size,View on GitHub (pinned to 4ed7a308f6)
Solutions
- Verify the WebDAV username/password has write permission on the target path
- Check that no file exists at a path segment that must be a directory
- Test manually: curl -X MKCOL against the parent path to see the server's response
- Correct the configured replica path/URL in the litestream config
Example fix
// before litestream.yml url: webdav://user:pass@dav.example.com/wrong-locked-path/db // after url: webdav://user:pass@dav.example.com/litestream/db
Defensive patterns
Strategy: try-catch
Validate before calling
client, _ := gowebdav.NewClient(serverURL, user, pass)
client.Connect() // fails early on auth problems
if err := client.MkdirAll(basePath, 0755); err != nil { return fmt.Errorf("precheck mkdir failed: %w", err) } Try / catch
if err := writeLTX(ctx); err != nil {
var pe *goserver.PathError
if strings.Contains(err.Error(), "cannot create parent directory") { verify credentials and that no file blocks the path segment }
} Prevention
- Pre-create the replica base directory and grant write access
- Test MKCOL permission with curl before configuring litestream
- Ensure no file exists where a directory segment is required
- Validate the configured replica path for typos
When it happens
Trigger: Uploading to a path whose parent cannot be created on the WebDAV server: insufficient WebDAV credentials, read-only collection, or the server rejecting MKCOL requests for the path prefix.
Common situations: WebDAV user lacking write rights to the target collection, misconfigured path in the litestream config (e.g. wrong base URL segment), servers that disallow MKCOL at certain depths, or an intermediate path segment existing as a FILE not a directory.
Understand the failure class
Background: mkdir permission denied (EACCES): failed to create directory errors explained — this error's family across 32 libraries.
Related errors
- create parent directory: %w
- recreate L0 directory: %w
- webdav: cannot delete path %q: %w
- abs: cannot delete ltx file %q: %w
- abs: cannot list blobs: %w
AI-assisted analysis of benbjohnson/litestream@4ed7a308f6 (2026-09-06).
Data as JSON: /api/errors/20e3a68980db2d89.
Report an issue: GitHub.