benbjohnson/litestream · error
webdav: cannot delete path %q: %w
Error message
webdav: cannot delete path %q: %w
What it means
DeleteAll removes the replica's entire path tree on the WebDAV server via client.RemoveAll(c.Path). If the removal fails for any reason other than 'path does not exist' (os.IsNotExist or gowebdav.IsErrNotFound), the error is wrapped with this message. It means the server refused or failed the recursive delete — typically permissions, a lock, or a transient server error.
Source
Thrown at webdav/replica_client.go:128
c.client.SetTimeout(c.Timeout)
if err := c.client.Connect(); err != nil {
c.client = nil
return nil, fmt.Errorf("webdav: cannot connect to server: %w", err)
}
return c.client, nil
}
func (c *ReplicaClient) DeleteAll(ctx context.Context) error {
client, err := c.init(ctx)
if err != nil {
return err
}
if err := client.RemoveAll(c.Path); err != nil && !os.IsNotExist(err) && !gowebdav.IsErrNotFound(err) {
return fmt.Errorf("webdav: cannot delete path %q: %w", c.Path, err)
}
internal.OperationTotalCounterVec.WithLabelValues(ReplicaClientType, "DELETE").Inc()
return nil
}
func (c *ReplicaClient) LTXFiles(ctx context.Context, level int, seek ltx.TXID, _ bool) (_ ltx.FileIterator, err error) {
client, err := c.init(ctx)
if err != nil {
return nil, err
}
dir := litestream.LTXLevelDir(c.Path, level)
files, err := client.ReadDir(dir)
if err != nil {
if os.IsNotExist(err) || gowebdav.IsErrNotFound(err) {
return ltx.NewFileInfoSliceIterator(nil), nilView on GitHub (pinned to 4ed7a308f6)
Solutions
- Check that the configured WebDAV user has DELETE/PUT permission on the path (try deleting a test file with curl -X DELETE)
- Inspect the wrapped error for the HTTP status: 403 = permissions, 507 = quota, 423 = locked
- Verify nothing else (another Litestream instance or sync client) is holding locks on the tree
- Retry the operation if the server error was transient; partial deletions are safe to re-run since not-found is tolerated
- As a last resort delete the collection server-side (admin panel/SSH) and re-run
Defensive patterns
Strategy: try-catch
Validate before calling
// probe delete permission with a throwaway object first
probe := c.Path + "/.litestream-permcheck"
if err := wc.Write(probe, strings.NewReader(""), nil); err != nil {
return fmt.Errorf("no write/delete permission on %s: %w", c.Path, err)
}
wc.Remove(probe) Try / catch
err := rc.DeleteAll(ctx)
if err != nil && strings.Contains(err.Error(), "cannot delete path") {
var sd *gowebdav.StatusError
if errors.As(err, &sd) && sd.Status == http.StatusForbidden {
// escalate permissions or clean up server-side
}
} Prevention
- Grant the WebDAV user DELETE rights on the replica collection
- Avoid concurrent clients (other backup tools) locking the same tree
- Monitor server quota (507) before large deletions
- Treat not-found as success in your own wrappers; only handle genuine server errors
When it happens
Trigger: Calling DeleteAll (directly or via 'litestream replicate' teardown / replica reset paths) when the WebDAV account lacks write/delete permission on the path, the server returns 403/500/507, or a file is locked by another client.
Common situations: WebDAV user without delete rights on the collection; read-only mount on the server; another backup client holding a lock; quota/server-side errors during large recursive deletes; partial deletes leaving the collection in a locked state.
Understand the failure class
Background: "API error: {status}" and "HTTP 401/403/404/429/5xx" errors: non-2xx HTTP responses explained — this error's family across 27 libraries.
Related errors
- abs: cannot delete ltx file %q: %w
- abs: cannot delete blob %q: %w
- gs: cannot delete object %q: %w
- oss: delete batch of %d objects: %w
- oss: delete all batch of %d objects: %w
AI-assisted analysis of benbjohnson/litestream@4ed7a308f6 (2026-09-06).
Data as JSON: /api/errors/aca67183af136326.
Report an issue: GitHub.