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
- Link Dozzle Cloud / set the API key so apiKeyFunc returns a non-empty value
- Check the Cloud configuration (env var or settings) on the Dozzle instance
- Handle ErrNotConfigured client-side by showing a 'link Cloud' prompt instead of a search request
- 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
- Set the Cloud API key in config/env before enabling search/alert UI
- Expose a feature flag so the UI can hide cloud search when unlinked
- Check errors.Is(err, ErrNotConfigured) to distinguish 'not linked' from real failures
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
- cloud search failed
- cloud dispatcher missing
- invalid credentials
- notifications are not configured on this host
- container not found
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)