benbjohnson/litestream · error

webdav: cannot connect to server: %w

Error message

webdav: cannot connect to server: %w

What it means

init performs a connectivity check via gowebdav's Connect() (a PROPFIND on the base URL) and wraps any failure with 'webdav: cannot connect to server'. The inner error carries the real cause: DNS failure, connection refused, TLS error, timeout, or an HTTP auth/status error.

Source

Thrown at webdav/replica_client.go:115

func (c *ReplicaClient) init(ctx context.Context) (_ *gowebdav.Client, err error) {
	c.mu.Lock()
	defer c.mu.Unlock()

	if c.client != nil {
		return c.client, nil
	}

	if c.URL == "" {
		return nil, fmt.Errorf("webdav url required")
	}

	c.client = gowebdav.NewClient(c.URL, c.Username, c.Password)

	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

View on GitHub (pinned to 4ed7a308f6)

Solutions

  1. Verify reachability manually: curl -u user:pass https://host/path/ (expect 207 Multi-Status)
  2. Check the URL scheme and port in the replica URL — http vs https mismatches are common
  3. Confirm credentials; a 401 from the server surfaces wrapped inside this error
  4. Inspect the wrapped error (%w) for the root cause: connection refused = server/port down, x509 = TLS trust issue, timeout = network/firewall
  5. For self-signed TLS, add the CA to the system trust store or configure the HTTP client appropriately

Example fix

// before: opaque failure at runtime
client, err := c.Init(ctx)
// after: fail fast at startup with the underlying cause
if _, err := c.Init(context.Background()); err != nil {
    log.Fatalf("webdav replica unreachable: %v", err) // inspect wrapped cause
}
Defensive patterns

Strategy: retry

Validate before calling

// preflight: verify the WebDAV endpoint answers before starting replication
req, _ := http.NewRequest(http.MethodPropfind, baseURL, nil)
req.SetBasicAuth(user, pass)
resp, err := httpClient.Do(req)
if err != nil { return fmt.Errorf("webdav unreachable: %w", err) }
resp.Body.Close()

Try / catch

_, err := rc.Init(ctx)
if err != nil && strings.Contains(err.Error(), "cannot connect to server") {
    // retry with backoff; surface the wrapped cause
    return retry.Do(func() error { _, err = rc.Init(ctx); return err }, retry.Attempts(3))
}

Prevention

When it happens

Trigger: Any replica operation (Init, Write, LTXFiles, DeleteAll) when the WebDAV server is unreachable, the URL/scheme/port is wrong, credentials are rejected, or TLS certificates fail validation.

Common situations: WebDAV server down or behind a NAT without port forwarding; self-signed certificate not trusted; wrong username/password (401); server URL using http where https is required (or vice versa); firewall blocking the port.

Understand the failure class

Background: ECONNREFUSED and "connection refused" / "could not connect to server" errors: what they mean and how to fix them — this error's family across 44 libraries.

Related errors


AI-assisted analysis of benbjohnson/litestream@4ed7a308f6 (2026-09-06). Data as JSON: /api/errors/6055ca15adf8a8e8. Report an issue: GitHub.