{"record":{"id":"f11d2d5bada5b75b","repo":"gotify/server","slug":"invalid-credentials","errorCode":null,"errorMessage":"invalid credentials","messagePattern":"invalid credentials","errorType":"http","errorClass":null,"httpStatus":401,"severity":"error","filePath":"api/session.go","lineNumber":79,"sourceCode":"func (a *SessionAPI) Login(ctx *gin.Context) {\n\tif !a.LocalAuthEnabled {\n\t\tctx.AbortWithError(403, errors.New(\"local authentication is disabled\"))\n\t\treturn\n\t}\n\n\tname, pass, ok := ctx.Request.BasicAuth()\n\tif !ok {\n\t\tctx.AbortWithError(401, errors.New(\"basic auth required\"))\n\t\treturn\n\t}\n\n\tuser, err := a.DB.GetUserByName(name)\n\tif err != nil {\n\t\tctx.AbortWithError(500, err)\n\t\treturn\n\t}\n\tif user == nil || !password.ComparePassword(user.Pass, []byte(pass)) {\n\t\tctx.AbortWithError(401, errors.New(\"invalid credentials\"))\n\t\treturn\n\t}\n\n\tclientParams := ClientParams{}\n\tif err := ctx.Bind(&clientParams); err != nil {\n\t\treturn\n\t}\n\n\televatedUntil := time.Now().Add(model.DefaultElevationDuration)\n\ttokenPublic, tokenPrivate := generateClientToken()\n\tclient := model.Client{\n\t\tName:                          clientParams.Name,\n\t\tToken:                         tokenPublic,\n\t\tUserID:                        user.ID,\n\t\tElevatedUntil:                 &elevatedUntil,\n\t\tExpiresAfterInactivitySeconds: auth.CookieMaxAge,\n\t}\n\tif success := successOrAbort(ctx, 500, a.DB.CreateClient(&client)); !success {","sourceCodeStart":61,"sourceCodeEnd":97,"githubUrl":"https://github.com/gotify/server/blob/14bfc256276775c425f988d621dccfe705de18ac/api/session.go#L61-L97","documentation":"Returned by Login after the Basic credentials were parsed but the lookup failed: either no user with that name exists (a.DB.GetUserByName returned nil) or password.ComparePassword does not match the stored hash. The handler deliberately returns a single 401 'invalid credentials' for both cases to avoid leaking which usernames exist.","triggerScenarios":"POST to the session/login endpoint with valid Basic header syntax but a wrong password; a username that does not exist in the database; a user created before a password-hashing scheme change so ComparePassword can no longer validate the stored hash; empty password bytes.","commonSituations":"Typo in username or password; password changed or reset elsewhere; database pointing at the wrong environment (staging user vs prod); migrating users without rehashing passwords; LDAP/external auth expected but local DB auth is what runs.","solutions":["Re-enter the username/password carefully; test the same credentials with curl -u","Verify the user exists in the DB (users table / GetUserByName) and reset the password if needed (admin UpdatePassword endpoint or CLI)","Confirm you are connected to the environment/database you expect","If hashes were migrated, re-hash/re-set passwords to match the current password.ComparePassword scheme"],"exampleFix":"// before\ncurl -X POST https://host/api/session -u 'jane:passwrod'\n// after\ncurl -X POST https://host/api/session -u 'jane:correct-password'","handlingStrategy":"validation","validationCode":"if (!username || !password) {\n  throw new Error('username and password required before calling login');\n}\n// optionally pre-check user existence via admin API if available","typeGuard":"function hasCredentials(c) {\n  return typeof c === 'object' && typeof c.username === 'string' && c.username.length > 0 && typeof c.password === 'string' && c.password.length > 0;\n}","tryCatchPattern":"try {\n  await api.login(username, password);\n} catch (e) {\n  if (e.status === 401) { showLoginFormError('Invalid username or password'); }\n  else { throw e; }\n}","preventionTips":["Surface a single generic 'invalid credentials' message; do not probe usernames","Verify environment/database when credentials that 'should work' fail","After hash-scheme migrations, force password resets for migrated accounts","Test credentials out-of-band with curl -u before blaming the client"],"tags":["http","authentication","password","credentials"],"backgroundTag":"invalid-login-credentials","analyzedSha":"14bfc256276775c425f988d621dccfe705de18ac","analyzedAt":"2026-09-05T12:52:36.781Z","contentChangedAt":"2026-09-05T12:52:36.781Z","schemaVersion":2},"datasetVersion":"2026-09-12T17:17:11.597Z"}