{"record":{"id":"e422991bb42ba991","repo":"AlexxIT/go2rtc","slug":"either-email-password-or-refresh-token-is-required","errorCode":null,"errorMessage":"either email/password or refresh_token is required","messagePattern":"either email/password or refresh_token is required","errorType":"http","errorClass":null,"httpStatus":400,"severity":"error","filePath":"internal/ring/ring.go","lineNumber":60,"sourceCode":"\t\t}\n\n\t\t// Try authentication (this will trigger 2FA if needed)\n\t\tif _, err = ringAPI.GetAuth(code); err != nil {\n\t\t\tif ringAPI.Using2FA {\n\t\t\t\t// Return 2FA prompt\n\t\t\t\tapi.ResponseJSON(w, map[string]interface{}{\n\t\t\t\t\t\"needs_2fa\": true,\n\t\t\t\t\t\"prompt\":    ringAPI.PromptFor2FA,\n\t\t\t\t})\n\t\t\t\treturn\n\t\t\t}\n\t\t\thttp.Error(w, err.Error(), http.StatusInternalServerError)\n\t\t\treturn\n\t\t}\n\t} else if refreshToken := query.Get(\"refresh_token\"); refreshToken != \"\" {\n\t\t// Refresh Token Flow\n\t\tif refreshToken == \"\" {\n\t\t\thttp.Error(w, \"either email/password or refresh_token is required\", http.StatusBadRequest)\n\t\t\treturn\n\t\t}\n\n\t\tvar err error\n\t\tringAPI, err = ring.NewRestClient(ring.RefreshTokenAuth{\n\t\t\tRefreshToken: refreshToken,\n\t\t}, nil)\n\n\t\tif err != nil {\n\t\t\thttp.Error(w, err.Error(), http.StatusInternalServerError)\n\t\t\treturn\n\t\t}\n\t} else {\n\t\thttp.Error(w, \"either email/password or refresh token is required\", http.StatusBadRequest)\n\t\treturn\n\t}\n\n\tdevices, err := ringAPI.FetchRingDevices()","sourceCodeStart":42,"sourceCodeEnd":78,"githubUrl":"https://github.com/AlexxIT/go2rtc/blob/c245815e75e2a5fd60b4290f12bfc04e55a984d3/internal/ring/ring.go#L42-L78","documentation":"GET /api/ring requires exactly one authentication method: email/password OR refresh_token. This 400 branch is reached when the refresh_token query parameter was selected (non-empty via query.Get) yet is effectively empty at validation — i.e. the handler rejected the request because neither a usable email nor a refresh token was supplied. It is a request-validation error, not a Ring API error.","triggerScenarios":"Calling /api/ring with neither email nor refresh_token (hits the else branch with the sibling message), or with a refresh_token parameter that is present but blank/whitespace, or a misspelled parameter name (e.g. refresh= instead of refresh_token=).","commonSituations":"Forgotten to paste the refresh token; copying the URL with an empty refresh_token=; parameter name typos; clients stripping query values; expecting the endpoint to work with no credentials at all.","solutions":["Provide a non-empty refresh_token: /api/ring?refresh_token=<token from a prior email/password login>","Or provide email and password to start the login/2FA flow and obtain a refresh token","Check the query parameter is spelled exactly refresh_token and is not empty","Store the refresh token returned by go2rtc (URL field) for future automatic logins"],"exampleFix":"// before\n/api/ring?refresh_token=\n// after\n/api/ring?refresh_token=eyJhbGciOi...","handlingStrategy":"validation","validationCode":"if refreshToken == \"\" && email == \"\" {\n    return errors.New(\"api/ring requires ?refresh_token=<token> or ?email=<email>&password=<pass>\")\n}","typeGuard":null,"tryCatchPattern":"u, _ := url.Parse(ringAPIURL)\nq := u.Query()\nif q.Get(\"refresh_token\") == \"\" && q.Get(\"email\") == \"\" {\n    return errors.New(\"ring source missing credentials\")\n}\n// proceed with the request once a credential parameter is present","preventionTips":["Persist the refresh_token returned by the first successful login","Spell the parameter exactly refresh_token","Validate query strings after copying source URLs from go2rtc's UI","Never send the ring API request with both credential sets absent"],"tags":["ring","validation","http"],"backgroundTag":"missing-required-argument","analyzedSha":"c245815e75e2a5fd60b4290f12bfc04e55a984d3","analyzedAt":"2026-09-07T11:47:02.965Z","contentChangedAt":"2026-09-07T11:47:02.965Z","schemaVersion":2},"datasetVersion":"2026-09-15T23:17:13.987Z"}