benbjohnson/litestream · error

replica url scheme required: %s

Error message

replica url scheme required: %s

What it means

ParseReplicaURLWithQuery requires every replica URL to carry a scheme; when url.Parse yields an empty scheme (bare path like "/path/to/replica" or "localhost:9000/bucket"), this error is returned. The library cannot pick a storage backend without a scheme.

Source

Thrown at replica_url.go:103

	if strings.HasPrefix(strings.ToLower(s), "s3://arn:") {
		scheme, host, urlPath, query, err := parseS3AccessPointURL(s)
		return scheme, host, urlPath, query, nil, err
	}

	u, err := url.Parse(s)
	if err != nil {
		return "", "", "", nil, nil, err
	}

	switch u.Scheme {
	case "file":
		scheme, u.Scheme = u.Scheme, ""
		// Remove query params from path for file URLs
		u.RawQuery = ""
		return scheme, "", path.Clean(u.String()), nil, nil, nil

	case "":
		return u.Scheme, u.Host, u.Path, nil, nil, fmt.Errorf("replica url scheme required: %s", s)

	default:
		return u.Scheme, u.Host, strings.TrimPrefix(path.Clean(u.Path), "/"), u.Query(), u.User, nil
	}
}

// parseS3AccessPointURL parses an S3 Access Point URL (s3://arn:...).
func parseS3AccessPointURL(s string) (scheme, host, urlPath string, query url.Values, err error) {
	const prefix = "s3://"
	if !strings.HasPrefix(strings.ToLower(s), prefix) {
		return "", "", "", nil, fmt.Errorf("invalid s3 access point url: %s", s)
	}

	arnWithPath := s[len(prefix):]

	// Split off query string if present
	var queryStr string
	if idx := strings.IndexByte(arnWithPath, '?'); idx != -1 {

View on GitHub (pinned to 4ed7a308f6)

Solutions

  1. Prefix the path with file:// for local replicas (file:///mnt/backup/db).
  2. Add the correct storage scheme to the URL (s3://, gs://, abs://, sftp://, webdav://).
  3. Validate the replica URL in config before starting litestream (scheme non-empty).
  4. Check env-var expansion in the config file didn't drop the scheme prefix.

Example fix

// before: config with bare path
replica: { url: "/mnt/backup/db" } // replica url scheme required
// after
replica: { url: "file:///mnt/backup/db" }
Defensive patterns

Strategy: validation

Validate before calling

u, err := url.Parse(replicaURL)
if err != nil { return err }
if u.Scheme == "" { return fmt.Errorf("replica URL %q needs a scheme (file://, s3://, gs://, ...)", replicaURL) }

Type guard

func hasReplicaScheme(rawURL string) bool {
    u, err := url.Parse(rawURL)
    return err == nil && u.Scheme != ""
}

Try / catch

if _, err := litestream.ParseReplicaURL(cfg.ReplicaURL); err != nil {
    return fmt.Errorf("invalid replica URL in config: %w", err)
}

Prevention

When it happens

Trigger: Passing a replica URL string with no scheme to ParseReplicaURL/NewReplicaClientFromURL — e.g. a filesystem path without the file:// prefix, or a host/bucket string with no s3:// prefix.

Common situations: YAML config with `url: /mnt/backup/db` instead of `file:///mnt/backup/db`; passing `mybucket/db` instead of `s3://mybucket/db`; env-substitution producing an empty scheme (`$REPLICA_TYPE://...` with REPLICAS_TYPE unset is a different case, but a URL of only a path hits this).

Understand the failure class

Background: "Invalid URL" errors: why new URL(), URI.parse, and reqwest::Url reject your string — missing scheme, whitespace, and bad path format — this error's family across 39 libraries.

Related errors


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