{"record":{"id":"bcad6ce9294395a5","repo":"getgrav/grav","slug":"user-account-with-this-username-already-exists-bcad6c","errorCode":null,"errorMessage":"User account with this username already exists","messagePattern":"User account with this username already exists","errorType":"validation","errorClass":"RuntimeException","httpStatus":null,"severity":"error","filePath":"system/src/Grav/Common/User/DataUser/User.php","lineNumber":142,"sourceCode":"        if (!$file || !$file->filename()) {\n            user_error(self::class . ': calling \\$user = new ' . self::class . \"() is deprecated since Grav 1.6, use \\$grav['accounts']->load(\\$username) or \\$grav['accounts']->load('') instead\", E_USER_DEPRECATED);\n        }\n\n        if ($file) {\n            $username = $this->filterUsername((string)$this->get('username'));\n\n            // Validate username to prevent path traversal attacks\n            if (!self::isValidUsername($username)) {\n                throw new \\RuntimeException('Invalid username: contains invalid characters or sequences');\n            }\n\n            if (!$file->filename()) {\n                $locator = Grav::instance()['locator'];\n\n                // Check if a user with this username already exists (prevent overwriting)\n                $existingFile = $locator->findResource('account://' . $username . YAML_EXT);\n                if ($existingFile) {\n                    throw new \\RuntimeException('User account with this username already exists');\n                }\n\n                $file->filename($locator->findResource('account://' . $username . YAML_EXT, true, true));\n            }\n\n            // if plain text password, hash it and remove plain text\n            $password = $this->get('password') ?? $this->get('password1');\n            if (null !== $password && '' !== $password) {\n                $password2 = $this->get('password2');\n                if (!\\is_string($password) || ($password2 && $password !== $password2)) {\n                    throw new \\RuntimeException('Passwords did not match.');\n                }\n\n                $this->set('hashed_password', Authentication::create($password));\n            }\n            $this->undef('password');\n            $this->undef('password1');\n            $this->undef('password2');","sourceCodeStart":124,"sourceCodeEnd":160,"githubUrl":"https://github.com/getgrav/grav/blob/6040efed04efa69b8209448ed81308e7c24147c2/system/src/Grav/Common/User/DataUser/User.php#L124-L160","documentation":"When saving a new DataUser account that has no target file yet, User::save() resolves account://<username>.yaml through the locator (line 142); if that file already exists it throws instead of silently overwriting. This is creation-time collision protection for file-based accounts: an existing account's data cannot be clobbered by a fresh save.","triggerScenarios":"Calling save() on an account object created with $grav['accounts']->load('') while account/<username>.yaml already exists; double submission of a registration form; re-running an import/migration that partially completed.","commonSituations":"Registration race or double-click on submit; non-idempotent migration scripts; case-sensitivity surprises where 'User1' collides with 'user1' on case-insensitive filesystems.","solutions":["Check existence first: if the locator finds account://<username>.yaml (or $grav['accounts'] reports the user), reject the submission as 'username taken'.","Make registration handlers idempotent: on this error, treat the account as created and log the user in rather than retrying the insert.","For migrations, pre-scan the accounts directory and skip/rename duplicates before saving."],"exampleFix":"// before\n$user = $grav['accounts']->load('');\n$user->set('username', $username);\n$user->merge($data);\n$user->save(); // throws: account/<username>.yaml exists\n\n// after\nif ($grav['locator']->findResource('account://' . $username . '.yaml')) {\n    throw new \\DomainException('Username is already taken.');\n}\n$user = $grav['accounts']->load('');\n$user->set('username', $username);\n$user->merge($data);\n$user->save();","handlingStrategy":"validation","validationCode":"$locator = $grav['locator'];\nif ($locator->findResource('account://' . $username . '.yaml') !== false) {\n    // reject: username already taken\n}","typeGuard":"function isUsernameAvailable(\\Grav\\Common\\Grav $grav, string $username): bool\n{\n    return false === $grav['locator']->findResource('account://' . $username . '.yaml');\n}","tryCatchPattern":"try {\n    $user->save();\n} catch (\\RuntimeException $e) {\n    if (str_contains($e->getMessage(), 'already exists')) {\n        // treat as 'username taken': re-show registration form\n    }\n    throw $e;\n}","preventionTips":["Check account existence before every create; treat the exception as a race-condition backstop.","Make registration handlers idempotent (double submit must not 500).","Use case-insensitive duplicate checks if your filesystem is case-insensitive."],"tags":["user-management","duplicate","registration","accounts","data-safety"],"backgroundTag":"username-already-exists","analyzedSha":"6040efed04efa69b8209448ed81308e7c24147c2","analyzedAt":"2026-08-17T05:07:31.593Z","schemaVersion":2},"datasetVersion":"2026-08-17T09:17:11.063Z"}