benbjohnson/litestream · error

heartbeat URL must be a valid HTTP or HTTPS URL

Error message

heartbeat URL must be a valid HTTP or HTTPS URL

What it means

ErrInvalidHeartbeatURL is returned by Config.Validate when the heartbeat-url config value is set but is not a parseable HTTP or HTTPS URL. Litestream sends periodic heartbeat pings to this URL, so a malformed value would silently break monitoring; validation rejects it up front via ConfigValidationError with Field "heartbeat-url".

Source

Thrown at cmd/litestream/main.go:61

func init() {
	bi, _ := debug.ReadBuildInfo()
	Version = resolveVersion(Version, bi)
}

// errStop is a terminal error for indicating program should quit.
var errStop = errors.New("stop")

// Sentinel errors for configuration validation
var (
	ErrInvalidSnapshotInterval         = errors.New("snapshot interval must be greater than 0")
	ErrInvalidSnapshotRetention        = errors.New("snapshot retention must be greater than 0")
	ErrInvalidCompactionInterval       = errors.New("compaction interval must be greater than 0")
	ErrInvalidSyncInterval             = errors.New("sync interval must be greater than 0")
	ErrInvalidL0Retention              = errors.New("l0 retention must not be negative")
	ErrInvalidL0RetentionCheckInterval = errors.New("l0 retention check interval must be greater than 0")
	ErrInvalidShutdownSyncTimeout      = errors.New("shutdown-sync-timeout must be >= 0")
	ErrInvalidShutdownSyncInterval     = errors.New("shutdown sync interval must be greater than 0")
	ErrInvalidHeartbeatURL             = errors.New("heartbeat URL must be a valid HTTP or HTTPS URL")
	ErrInvalidHeartbeatInterval        = errors.New("heartbeat interval must be at least 1 minute")
	ErrConfigFileNotFound              = errors.New("config file not found")
)

// ConfigValidationError wraps a validation error with additional context
type ConfigValidationError struct {
	Err   error
	Field string
	Value interface{}
}

func (e *ConfigValidationError) Error() string {
	if e.Value != nil {
		return fmt.Sprintf("%s: %v (got %v)", e.Field, e.Err, e.Value)
	}
	return fmt.Sprintf("%s: %v", e.Field, e.Err)
}

View on GitHub (pinned to 4ed7a308f6)

Solutions

  1. Prefix the URL with http:// or https:// in the config
  2. Verify the value with the URL parser you expect: it must parse and have Scheme http or https
  3. Remove heartbeat-url entirely if you do not want heartbeats

Example fix

# before
heartbeat-url: monitoring.internal:8080/ping
# after
heartbeat-url: https://monitoring.internal:8080/ping
Defensive patterns

Strategy: validation

Validate before calling

func validHeartbeatURL(s string) bool {
    u, err := url.Parse(s)
    return err == nil && (u.Scheme == "http" || u.Scheme == "https") && u.Host != ""
}
// call before writing config / at startup

Prevention

When it happens

Trigger: Setting heartbeat-url in the YAML config (or the c.HeartbeatURL field) to a value that fails isValidHeartbeatURL: a scheme-less host ("myhost/ping"), an unsupported scheme ("ftp://...", "tcp://..."), or a string that does not parse as a URL at all.

Common situations: Typos like heartbeat-url: myserver:8080/ping (missing scheme), copying an internal service URL that starts with a scheme other than http/https, or quoting issues in YAML that leave stray characters in the value.

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/4b6c3fec40b6ef5e. Report an issue: GitHub.