{"record":{"id":"ca3045e9105f68d9","repo":"amir20/dozzle","slug":"empty-key-received","errorCode":null,"errorMessage":"empty key received","messagePattern":"empty key received","errorType":"http","errorClass":null,"httpStatus":500,"severity":"error","filePath":"internal/web/cloud.go","lineNumber":95,"sourceCode":"\tdefer resp.Body.Close()\n\n\tif resp.StatusCode != http.StatusOK {\n\t\tbody, _ := io.ReadAll(io.LimitReader(resp.Body, 1024))\n\t\tlog.Error().Int(\"status\", resp.StatusCode).Str(\"body\", string(body)).Msg(\"Token exchange failed\")\n\t\thttp.Error(w, \"token exchange failed\", http.StatusInternalServerError)\n\t\treturn\n\t}\n\n\tvar tokenResp exchangeTokenResponse\n\tif err := json.NewDecoder(resp.Body).Decode(&tokenResp); err != nil {\n\t\tlog.Error().Err(err).Msg(\"Failed to decode token response\")\n\t\thttp.Error(w, \"failed to decode token response\", http.StatusInternalServerError)\n\t\treturn\n\t}\n\n\tif tokenResp.Key == \"\" {\n\t\tlog.Error().Msg(\"Empty key received\")\n\t\thttp.Error(w, \"empty key received\", http.StatusInternalServerError)\n\t\treturn\n\t}\n\n\tvar expiresAt *time.Time\n\tif tokenResp.ExpiresAt != nil {\n\t\tparsed, err := time.Parse(time.RFC3339, *tokenResp.ExpiresAt)\n\t\tif err != nil {\n\t\t\tlog.Warn().Err(err).Str(\"expiresAt\", *tokenResp.ExpiresAt).Msg(\"Failed to parse expiresAt, ignoring\")\n\t\t} else {\n\t\t\texpiresAt = &parsed\n\t\t}\n\t}\n\n\t// Save cloud config (also creates the cloud dispatcher and broadcasts to agents)\n\tcc := &notification.CloudConfig{\n\t\tAPIKey:    tokenResp.Key,\n\t\tPrefix:    tokenResp.Prefix,\n\t\tExpiresAt: expiresAt,","sourceCodeStart":77,"sourceCodeEnd":113,"githubUrl":"https://github.com/amir20/dozzle/blob/d9463cbe21874e44ab79db6fa63e746ca7d22928/internal/web/cloud.go#L77-L113","documentation":"After successfully decoding the token response, cloudCallback requires a non-empty `key` field; this key is what dozzle stores to authenticate with the cloud service. An empty key means the exchange technically succeeded but produced no credential, so dozzle refuses it with a 500.","triggerScenarios":"The cloud exchange endpoint returns 200 with valid JSON but `key` is \"\" or the field is absent, e.g. account not provisioned, subscription inactive, or a cloud-side bug returning a success envelope without a key.","commonSituations":"Dozzle Cloud subscription lapsed or not active for this account; API key revoked on the cloud side while old tokens still validate; mismatched cloud service version returning a new response shape.","solutions":["Verify your Dozzle Cloud account/subscription is active and an API key is provisioned.","Re-run the cloud connect flow to request a fresh exchange.","Check dozzle server logs for the `Empty key received` entry and contact support if the account is valid."],"exampleFix":"// before\n{\"key\":\"\"}\n// after\n{\"key\":\"abc123...\",\"expiresAt\":\"2026-01-01T00:00:00Z\"}","handlingStrategy":"validation","validationCode":"const data = await res.json();\nif (!data.key) {\n  // stop: exchange returned no credential; verify account/subscription before retry\n}","typeGuard":"function hasKey(v: { key?: string }): v is { key: string } {\n  return typeof v.key === 'string' && v.key.length > 0;\n}","tryCatchPattern":null,"preventionTips":["Keep the Dozzle Cloud subscription active and the API key provisioned.","Re-run the connect flow rather than reusing stale state.","Report persistent empty-key responses to support with server logs."],"tags":["cloud","credentials","empty-value"],"backgroundTag":"empty-required-field","analyzedSha":"d9463cbe21874e44ab79db6fa63e746ca7d22928","analyzedAt":"2026-09-07T10:08:55.855Z","contentChangedAt":"2026-09-07T10:08:55.855Z","schemaVersion":2},"datasetVersion":"2026-09-16T04:17:20.429Z"}