amir20/dozzle · warning · ErrInvalidCredentials
invalid credentials
Error message
invalid credentials
What it means
ErrInvalidCredentials is the sentinel error returned by SimpleAuth.CreateToken in internal/auth/simple.go when the username cannot be found in the user database or the bcrypt-style password comparison fails. The HTTP layer maps it to a failed login attempt.
Solutions
- Double-check the username and password against ./data/users.yml (usernames are case-sensitive keys)
- Regenerate the password entry with a properly hashed value matching the expected hash format
- Verify users.yml parses as valid YAML with correct indentation for each user entry
- Confirm the auth provider is `simple` mode and the file path configured is the one actually loaded
Defensive patterns
Strategy: validation
Validate before calling
// before calling login
const users, _ := loadUsersYAML(path) // verify file parses and username key exists
if _, ok := users[username]; !ok {
return fmt.Errorf("user %q not present in users.yml", username)
} Try / catch
token, err := auth.CreateToken(username, password)
if errors.Is(err, auth.ErrInvalidCredentials) {
http.Error(w, "invalid username or password", http.StatusUnauthorized)
return
} Prevention
- Keep users.yml valid YAML with one correctly indented entry per user
- Verify the username key case exactly matches the file
- Regenerate password hashes with the supported hashing scheme after manual edits
- Check errors.Is(err, ErrInvalidCredentials) rather than string comparison
When it happens
Trigger: CreateToken(username, password) is called (login endpoint) and find(username) misses, or CompareHashAndPassword(user.Password, password) returns false.
Common situations: users.yml doesn't exist or has no such user; the password is wrong; users.yml was edited but the entry was malformed/removed; client sends an email-style username while the file has a plain name (or vice versa); YAML indentation mistakes produce empty/partial user records.
Understand the failure class
Background: "User not found", "Invalid user", and "does not exist": what missing-user lookup errors mean across Rocket.Chat, LiteLLM, Phabricator, rustfs, and pnpm — this error's family across 10 libraries.
- Authentication and authorization failures — expired tokens, bad credentials, and missing scopes.
Related errors
- cloud: no API key configured
- notifications are not configured on this host
- container not found
- invalid format: key is empty
- registry requires authentication
AI-assisted analysis of amir20/dozzle@d9463cbe21 (2026-09-07).
Data as JSON: /api/errors/6d9bd0467aaa8c78.
Report an issue: GitHub.
Appendix: source
Thrown at internal/auth/simple.go:26
"net/http"
"slices"
"sync"
"time"
"github.com/go-chi/jwtauth/v5"
"github.com/rs/zerolog/log"
)
type simpleAuthContext struct {
UserDatabase UserDatabase
tokenAuth *jwtauth.JWTAuth
ttl time.Duration
// UserDatabase.Find reloads users.yml in place, and the middleware now calls it
// on every request, so the reload has to be serialized.
mu sync.Mutex
}
var ErrInvalidCredentials = errors.New("invalid credentials")
func NewSimpleAuth(userDatabase UserDatabase, ttl time.Duration) *simpleAuthContext {
// Hash the users in a stable order. Ranging over the map directly makes the
// digest depend on Go's randomized map iteration order, so any users.yml with
// more than one user derives a different signing key on every start and
// silently invalidates every session on restart.
h := sha256.New()
for _, username := range slices.Sorted(maps.Keys(userDatabase.Users)) {
user := userDatabase.Users[username]
h.Write([]byte(user.Password))
h.Write([]byte(user.RolesConfigured))
}
tokenAuth := jwtauth.New("HS256", h.Sum(nil), nil)
return &simpleAuthContext{
UserDatabase: userDatabase,
tokenAuth: tokenAuth,View on GitHub (pinned to d9463cbe21)