multica-ai/multica · error

value must be an http(s) URL

Error message

value must be an http(s) URL

What it means

validatePropertyValue enforces that URL-type property values parse as absolute http or https URLs with a host. Values that fail url.Parse, use another scheme (ftp:, mailto:, file:), or lack a host ("example.com", "/relative") are rejected with this message so the stored URL is always clickable and safe to render.

Source

Thrown at server/internal/handler/property.go:340

		if strings.TrimSpace(s) == "" {
			return nil, errors.New("value cannot be empty (use DELETE to unset a property)")
		}
		if utf8.RuneCountInString(s) > maxPropertyTextValueLen {
			return nil, fmt.Errorf("value must be %d characters or fewer", maxPropertyTextValueLen)
		}
		return json.Marshal(sanitizeNullBytes(s))
	case "url":
		s, ok := v.(string)
		if !ok {
			return nil, errors.New("value must be a URL string")
		}
		s = strings.TrimSpace(s)
		if len(s) > maxPropertyURLValueLen {
			return nil, fmt.Errorf("value must be %d characters or fewer", maxPropertyURLValueLen)
		}
		u, err := url.Parse(s)
		if err != nil || (u.Scheme != "http" && u.Scheme != "https") || u.Host == "" {
			return nil, errors.New("value must be an http(s) URL")
		}
		return json.Marshal(s)
	case "number":
		if _, ok := v.(float64); !ok {
			return nil, errors.New("value must be a number")
		}
		return json.Marshal(v)
	case "checkbox":
		if _, ok := v.(bool); !ok {
			return nil, errors.New("value must be true or false")
		}
		return json.Marshal(v)
	case "date":
		s, ok := v.(string)
		if !ok {
			return nil, errors.New("value must be a date string in YYYY-MM-DD format")
		}
		if _, err := time.Parse("2006-01-02", s); err != nil {

View on GitHub (pinned to 2c0912b6ec)

Solutions

  1. Prefix the scheme: normalize input to https:// when missing
  2. Use an absolute http(s) URL including host: "https://example.com/path"
  3. If you need relative links, mailto, or custom schemes, switch the property definition to type "text" instead of "url"

Example fix

// before
{ value: "example.com/docs" }
// after
{ value: "https://example.com/docs" }
Defensive patterns

Strategy: validation

Validate before calling

function toHttpUrl(input: string): string | null {
  const s = input.trim();
  const withScheme = /^https?:\/\//i.test(s) ? s : `https://${s}`;
  try {
    const u = new URL(withScheme);
    return u.host ? u.href : null;
  } catch { return null; }
}
const normalized = toHttpUrl(value);
if (!normalized) showFieldError('Enter an absolute http(s) URL');

Type guard

function isHttpUrl(s: string): boolean {
  try { const u = new URL(s); return (u.protocol === 'http:' || u.protocol === 'https:') && u.host !== ''; }
  catch { return false; }
}

Prevention

When it happens

Trigger: PUT/PATCH a url property with "ftp://host/x", "mailto:a@b.c", "example.com/path" (no scheme), "https://" (no host), or a malformed string that fails url.Parse. Also triggered by URLs with whitespace inside since only leading/trailing spaces are trimmed.

Common situations: Users pasting bare domains without https://; forms intended for internal links storing relative paths; mailto links stored in a url field because the definition type was chosen incorrectly.

Related errors


AI-assisted analysis of multica-ai/multica@2c0912b6ec (2026-08-15). Data as JSON: /api/errors/cb6d5366789a5706. Report an issue: GitHub.