gastownhall/beads · error

remote URL must not start with a dash

Error message

remote URL must not start with a dash

What it means

ValidateRemoteURL() rejects URLs beginning with '-' because exec.Command passes arguments directly to the dolt CLI — a URL like '-oProxyCommand=...' could be parsed as a flag (CLI flag injection). This is a deliberate security-boundary check applied to all remote URLs before they reach command arguments.

Source

Thrown at internal/remotecache/url.go:93

// that could be interpreted as CLI flags.
//
// This is a security boundary — all remote URLs should pass through this
// before reaching exec.Command arguments or SQL parameters.
func ValidateRemoteURL(rawURL string) error {
	if rawURL == "" {
		return fmt.Errorf("remote URL cannot be empty")
	}

	// Reject control characters (null bytes, newlines, tabs, etc.)
	for i, c := range rawURL {
		if c < 0x20 || c == 0x7f {
			return fmt.Errorf("remote URL contains control character at position %d (0x%02x)", i, c)
		}
	}

	// Reject leading dash (CLI flag injection via exec.Command arguments)
	if strings.HasPrefix(rawURL, "-") {
		return fmt.Errorf("remote URL must not start with a dash")
	}

	// SCP-style URLs (user@host:path) are validated separately
	if gitSSHPattern.MatchString(rawURL) {
		return validateSCPURL(rawURL)
	}

	// Parse as standard URL
	return validateSchemeURL(rawURL)
}

// validateSchemeURL validates a scheme-based URL (https://, dolthub://, etc.)
func validateSchemeURL(rawURL string) error {
	// net/url doesn't understand git+ssh:// etc., so we normalize first
	normalizedURL := rawURL
	scheme := ""
	if idx := strings.Index(rawURL, "://"); idx > 0 {
		scheme = rawURL[:idx]

View on GitHub (pinned to 71377f2769)

Solutions

  1. Correct the call/config so the remote URL (starting with a scheme like dolthub:// or https://) is passed as the URL, and flags use their own flags.
  2. Inspect config/env sources for a leading '-' typo and remove it.
  3. If you need to pass something dash-prefixed, it is not a URL — use the proper parameter/flag instead.
  4. Add a caller-side check that the remote URL contains '://' or matches SCP-style user@host:path before invoking.

Example fix

// before: flag accidentally passed as URL
_, err := cache.Ensure(ctx, "-verbose")
// after: guard and pass a real URL
if !strings.Contains(remoteURL, "://") && !strings.Contains(remoteURL, "@") {
    return fmt.Errorf("%q is not a remote URL", remoteURL)
}
_, err = cache.Ensure(ctx, remoteURL)
Defensive patterns

Strategy: validation

Validate before calling

func looksLikeRemoteURL(s string) bool {
    if strings.HasPrefix(s, "-") {
        return false
    }
    return remotecache.IsRemoteURL(s)
}
// before calling Ensure:
if !looksLikeRemoteURL(arg) {
    return fmt.Errorf("%q is not a remote URL (did you swap flag and URL?)", arg)
}

Try / catch

if err := cache.Ensure(ctx, remoteURL); err != nil {
    if strings.Contains(err.Error(), "must not start with a dash") {
        return fmt.Errorf("argument parsing bug: %q used as URL; check flag order", remoteURL)
    }
    return err
}

Prevention

When it happens

Trigger: ValidateRemoteURL (via Ensure or ValidateRemoteURLWithPatterns) receives a string whose first character is '-', e.g. a mangled config value, an argument-order bug where a flag was passed where the URL belongs, or a deliberately crafted input.

Common situations: Swapped CLI arguments (bd sync --remote -f); config file where the value got merged with a flag; hand-edited config with a stray dash; scripts passing options positionally into the URL slot.

Related errors


AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30). Data as JSON: /api/errors/7e23ddd9c95736f9. Report an issue: GitHub.