{"record":{"id":"58f86b59be13cb69","repo":"glanceapp/glance","slug":"usernames-must-be-at-least-3-characters","errorCode":null,"errorMessage":"usernames must be at least 3 characters","messagePattern":"usernames must be at least 3 characters","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"internal/glance/config.go","lineNumber":466,"sourceCode":"// one of them, which doesn't modify the data and only checks for logical errors\n// and then again when creating the application which does modify the data and do\n// further validation. Would be better if validation was done in a single place.\nfunc isConfigStateValid(config *config) error {\n\tif len(config.Pages) == 0 {\n\t\treturn fmt.Errorf(\"no pages configured\")\n\t}\n\n\tif len(config.Auth.Users) > 0 && config.Auth.SecretKey == \"\" {\n\t\treturn fmt.Errorf(\"secret-key must be set when users are configured\")\n\t}\n\n\tfor username := range config.Auth.Users {\n\t\tif username == \"\" {\n\t\t\treturn fmt.Errorf(\"user has no name\")\n\t\t}\n\n\t\tif len(username) < 3 {\n\t\t\treturn errors.New(\"usernames must be at least 3 characters\")\n\t\t}\n\n\t\tuser := config.Auth.Users[username]\n\n\t\tif user.Password == \"\" {\n\t\t\tif user.PasswordHashString == \"\" {\n\t\t\t\treturn fmt.Errorf(\"user %s must have a password or a password-hash set\", username)\n\t\t\t}\n\t\t} else if len(user.Password) < 6 {\n\t\t\treturn fmt.Errorf(\"the password for %s must be at least 6 characters\", username)\n\t\t}\n\t}\n\n\tif config.Server.AssetsPath != \"\" {\n\t\tif _, err := os.Stat(config.Server.AssetsPath); os.IsNotExist(err) {\n\t\t\treturn fmt.Errorf(\"assets directory does not exist: %s\", config.Server.AssetsPath)\n\t\t}\n\t}","sourceCodeStart":448,"sourceCodeEnd":484,"githubUrl":"https://github.com/glanceapp/glance/blob/91324e8de762702e97b0ac5c8e36271d644d8642/internal/glance/config.go#L448-L484","documentation":"Glance collects system info for the System widget and wraps every failure from gopsutil's mem.SwapMemory() as a collected warning. The error text is prefixed with 'getting swap memory info' and the raw gopsutil error follows. On non-fatal failures SwapIsAvailable stays false and the widget renders without swap stats.","triggerScenarios":"mem.SwapMemory() is called during sysinfo collection (System widget with swap fields enabled). It fails when the platform's swap source is unreadable: /proc/swaps missing or unreadable on Linux, WMI/swap query failing on Windows, or unsupported behavior in minimal containers.","commonSituations":"Running the Glance container image with /proc not mounted or masked by systemd (PrivateMounts), a hardened/seccomp environment blocking the sysinfo call, or exotic BSD setups. On normal hosts swap data almost always resolves.","solutions":["If running in Docker/Podman, ensure /proc is mounted read-only into the container (it is by default; check for -v overrides or maskedPaths)","On the host, verify with: cat /proc/swaps — if that fails, fix procfs mounting/permissions","If the environment genuinely has no readable swap source, accept the degraded output: hide swap fields in the widget config so the warning is not surfaced","Check the widget's error stream (errs slice) in logs to confirm the underlying gopsutil cause"],"exampleFix":"// before (docker-compose with restricted proc)\nservices:\n  glance:\n    volumes:\n      - /proc:/proc:ro\n// after: remove any proc masking so gopsutil can read /proc/swaps\nservices:\n  glance:\n    volumes:\n      - /proc:/proc:ro   # default mount, do not override with :ro on a masked /proc","handlingStrategy":"try-catch","validationCode":"v, err := mem.SwapMemory()\nif err != nil {\n    // swap unavailable; degrade gracefully\n    log.Printf(\"swap info unavailable: %v\", err)\n}","typeGuard":null,"tryCatchPattern":"Go idiom: check the returned error immediately and treat swap as optional. Glance already does this — SwapIsAvailable stays false and the error joins the collected errs slice, so callers render conditionally on info.Memory.SwapIsAvailable.","preventionTips":["In containers, always mount /proc read-only so gopsutil can read /proc/swaps","Design widget/config rendering to hide swap fields when SwapIsAvailable is false instead of assuming data","Treat sysinfo errors as warnings: log the errs slice but never fail the whole page render"],"tags":["go","gopsutil","system-info","swap","containers"],"backgroundTag":null,"analyzedSha":"91324e8de762702e97b0ac5c8e36271d644d8642","analyzedAt":"2026-08-15T14:12:54.279Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}