knadh/listmonk · error

missing azure event grid request context

Error message

missing azure event grid request context

What it means

verifyAuth, called by ProcessBounce, authenticates Azure Event Grid deliveries only when a shared secret is configured. This error is returned when a shared secret IS configured but the function receives a nil *http.Request, meaning there is no request context (query param or header) against which to verify the secret. It protects the strict-auth path from silently passing or panicking.

Source

Thrown at internal/bounce/webhooks/azure.go:153

		})
	}

	return out, nil
}

func (a *Azure) verifyAuth(req *http.Request) error {
	const (
		defaultSecretHeader = "X-Listmonk-Webhook-Secret"
		querySecretParam    = "code"
	)

	// If no local credential is configured, allow webhook payloads.
	if a.sharedSecret == "" {
		return nil
	}

	if req == nil {
		return errors.New("missing azure event grid request context")
	}

	querySecret := strings.TrimSpace(req.URL.Query().Get(querySecretParam))
	if secretsEqual(a.sharedSecret, querySecret) {
		return nil
	}

	headerName := a.sharedSecretHeader
	if headerName == "" {
		headerName = defaultSecretHeader
	}
	headerSecret := strings.TrimSpace(req.Header.Get(headerName))
	if secretsEqual(a.sharedSecret, headerSecret) {
		return nil
	}

	return errors.New("invalid azure event grid shared secret")
}

View on GitHub (pinned to 670c01717d)

Solutions

  1. Pass the original inbound *http.Request to ProcessBounce so verifyAuth can read the secret from the query param or header.
  2. If invoking outside HTTP (queue replay), fetch the stored secret value and provide a reconstructed request, or temporarily run that path without a configured shared secret.
  3. In tests, either set no shared secret (NewAzure with empty secret) or pass an httptest.Request with the secret attached.
  4. Review call sites of ProcessBounce to ensure none omit the request after the auth feature was added.

Example fix

// before
bounces, err := azure.ProcessBounce(body, nil)
// after
bounces, err := azure.ProcessBounce(body, c.Request())
Defensive patterns

Strategy: try-catch

Validate before calling

if req == nil && os.Getenv("AZURE_WEBHOOK_SECRET") != "" {
	// shared secret configured but no request context: refuse before calling ProcessBounce
}

Type guard

func canVerifyAzure(azure *webhooks.Azure, req *http.Request) bool {
	return req != nil // or reflect on configured secret if exposed
}

Try / catch

bounces, err := azure.ProcessBounce(body, req)
if err != nil {
	if strings.Contains(err.Error(), "missing azure event grid request context") {
		log.Printf("azure webhook called without request context; check call site: %v", err)
	}
	return err
}

Prevention

When it happens

Trigger: The Azure webhook is constructed with a shared secret, but ProcessBounce is invoked (e.g. programmatically or from a queue worker replaying stored payloads) without passing the original *http.Request, so verifyAuth gets nil.

Common situations: Replaying stored webhook bodies in tests or a redelivery worker without the original request; refactoring changed the call site to drop the request parameter; unit tests calling ProcessBounce directly while the deployment has a shared secret configured.

Related errors


AI-assisted analysis of knadh/listmonk@670c01717d (2026-09-01). Data as JSON: /api/errors/35f1c889e7f6fac8. Report an issue: GitHub.