micro/go-micro · error

invalid push callback url

Error message

invalid push callback url: %w

What it means

checkPushURL parses the raw callback URL and then runs it through the dispatcher's effective policy: the user-supplied Options.AllowPushURL if set, otherwise the default SSRF-safe policy. If url.Parse fails (malformed URL) this wrapped error is returned. All push registrations and deliveries pass through this check.

Solutions

  1. Fix the callback URL so it is valid RFC 3986 syntax (scheme://host/path).
  2. URL-encode any special characters (spaces, %, control chars) before passing the URL.
  3. Log/inspect the underlying error (%w) to identify the exact parse failure before retrying.

Example fix

// before
setPushConfig(task, "http://host with space/cb")
// after
setPushConfig(task, "http://host-without-space/cb") // or url.QueryEscape dynamic parts
Defensive patterns

Strategy: validation

Validate before calling

if _, err := url.Parse(callbackURL); err != nil {
    return fmt.Errorf("invalid callback URL before registration: %w", err)
}

Type guard

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

Try / catch

err := d.CheckPushURL(raw)
var urlErr *url.Error
if errors.As(err, &urlErr) {
    log.Printf("push callback URL malformed: %v", urlErr)
}

Prevention

When it happens

Trigger: Calling setPushConfig or triggering deliverPush with a callback URL string that url.Parse cannot parse, e.g. containing control characters, 'http://%zz', or other malformed syntax.

Common situations: Typo or unencoded characters in the callback URL; environment-variable substitution producing a broken URL; concatenating host and path without a separator.

Related errors


AI-assisted analysis of micro/go-micro@24529f1404 (2026-09-01). Data as JSON: /api/errors/f569a1de501d1102. Report an issue: GitHub.

Appendix: source

Thrown at gateway/a2a/pushsecurity.go:114

// pushGuardClient is the HTTP client used for default-policy push delivery. Its
// dialer refuses connections to blocked addresses at connect time.
var pushGuardClient = &http.Client{
	Timeout: 10 * time.Second,
	Transport: &http.Transport{
		Proxy: http.ProxyFromEnvironment,
		DialContext: (&net.Dialer{
			Timeout: 5 * time.Second,
			Control: pushDialControl,
		}).DialContext,
	},
}

// checkPushURL validates a callback URL against the dispatcher's effective
// policy (Options.AllowPushURL, or the default SSRF-safe policy).
func (d *dispatcher) checkPushURL(raw string) error {
	u, err := url.Parse(raw)
	if err != nil {
		return fmt.Errorf("invalid push callback url: %w", err)
	}
	policy := d.allowPushURL
	if policy == nil {
		policy = defaultPushURLPolicy
	}
	return policy(u)
}

// pushClient is the HTTP client deliverPush uses: the guarded client under the
// default policy, or the default client when an operator has taken over the
// policy via Options.AllowPushURL (they own the trust decision then).
func (d *dispatcher) pushClient() *http.Client {
	if d.guardPushDial {
		return pushGuardClient
	}
	return http.DefaultClient
}

View on GitHub (pinned to 24529f1404)