amir20/dozzle · info · ErrNotConfigured

cloud: no API key configured

Error message

cloud: no API key configured

What it means

ErrNotConfigured in internal/cloud/search.go signals that Cloud search/alert calls (SearchLogs, GetAlerts) were made but apiKeyFunc returned an empty string, i.e. Dozzle Cloud was never linked. The HTTP layer maps it to a 503 so callers know the feature is unavailable rather than broken.

Solutions

  1. Link Dozzle Cloud / set the API key so apiKeyFunc returns a non-empty value
  2. Check the Cloud configuration (env var or settings) on the Dozzle instance
  3. Handle ErrNotConfigured client-side by showing a 'link Cloud' prompt instead of a search request
  4. Gate search/alert UI on a config feature flag when no API key is present

Example fix

apiKey := c.apiKeyFunc()
if apiKey == "" {
    return nil, ErrNotConfigured // caller maps to 503
}
Defensive patterns

Strategy: fallback

Validate before calling

// before issuing a cloud search from the frontend
if (!config.features.cloudSearch) {
  showCloudLinkPrompt();
  return;
}

Try / catch

alerts, err := client.GetAlerts(ctx)
if errors.Is(err, cloud.ErrNotConfigured) {
    return nil, status.Error(codes.Unavailable, "cloud not linked") // or serve local results
}

Prevention

When it happens

Trigger: cloudSearchLogs or cloudAlerts invoked with no API key: the user hasn't linked Cloud, the API-key env/config is unset, or the key was cleared from settings before a search request arrived.

Common situations: Frontend calls log search before the user completes Cloud linking; DOZZLE_API_KEY (or equivalent config) missing in the deployment; key removed after subscription lapse while UI still exposes search.

Understand the failure class

Background: "API key is required" / "API key not found" / "No API key was set": the missing-api-key error family across 16 libraries — this error's family across 16 libraries.

Related errors


AI-assisted analysis of amir20/dozzle@d9463cbe21 (2026-09-07). Data as JSON: /api/errors/80474f1e53eca803. Report an issue: GitHub.

Appendix: source

Thrown at internal/cloud/search.go:46

// instance's (user_id, api_key_id) — Cloud derives those from the auth
// metadata, never the request body.
type SearchLogHit struct {
	TimestampNs   int64  `json:"ts"`
	HostID        string `json:"hostId"`
	ContainerID   string `json:"containerId"`
	ContainerName string `json:"containerName"`
	Message       string `json:"message"`
	Stream        string `json:"stream"`
	Level         string `json:"level"`
	// LogID is Dozzle's FNV-32a hash of the original line. Lets the UI
	// build deep-links matching "Copy permalink" output. Omitted when the
	// row predates indexing (older Dozzle clients sent 0).
	LogID uint32 `json:"logId,omitempty"`
}

// ErrNotConfigured is returned when SearchLogs is called but no Cloud API key
// is available (the user hasn't linked Cloud yet). Callers map this to a 503.
var ErrNotConfigured = errors.New("cloud: no API key configured")

// unaryServiceClient returns a (lazily dialed) reusable gRPC client. The
// underlying conn is shared across every Dozzle-initiated unary call so we pay
// the TLS handshake once per process — not once per keystroke or scroll.
func (c *Client) unaryServiceClient() (pb.CloudToolServiceClient, error) {
	c.unaryConnMu.Lock()
	defer c.unaryConnMu.Unlock()
	if c.unaryClient != nil {
		return c.unaryClient, nil
	}
	var creds grpc.DialOption
	if c.plaintext {
		creds = grpc.WithTransportCredentials(insecure.NewCredentials())
	} else {
		creds = grpc.WithTransportCredentials(credentials.NewClientTLSFromCert(nil, ""))
	}
	conn, err := grpc.NewClient(c.target, creds)
	if err != nil {

View on GitHub (pinned to d9463cbe21)