Zie619/n8n-workflows · error · ValueError

Username or email already exists

Error message

Username or email already exists

What it means

UserManager.create_user() raises ValueError('Username or email already exists') when its pre-check SELECT COUNT(*) on the users table finds a row with the same username OR email. SQLite has no unique constraint enforcing this, so the check is purely application-level; the FastAPI register endpoint catches the ValueError and maps it to HTTP 400 with this detail.

Source

Thrown at src/user_management.py:175

        return hashlib.sha256(password.encode()).hexdigest()

    def verify_password(self, password: str, hashed: str) -> bool:
        """Verify password against hash."""
        return self.hash_password(password) == hashed

    def create_user(self, user_data: UserCreate) -> User:
        """Create a new user."""
        conn = sqlite3.connect(self.db_path)
        cursor = conn.cursor()

        try:
            # Check if username or email already exists
            cursor.execute(
                "SELECT COUNT(*) FROM users WHERE username = ? OR email = ?",
                (user_data.username, user_data.email),
            )
            if cursor.fetchone()[0] > 0:
                raise ValueError("Username or email already exists")

            password_hash = self.hash_password(user_data.password)

            cursor.execute(
                """
                INSERT INTO users (username, email, full_name, password_hash, role)
                VALUES (?, ?, ?, ?, ?)
            """,
                (
                    user_data.username,
                    user_data.email,
                    user_data.full_name,
                    password_hash,
                    user_data.role,
                ),
            )

            user_id = cursor.lastrowid

View on GitHub (pinned to 94007c1445)

Solutions

  1. Register with a different username and/or email — the check matches either column
  2. If re-running a seed script, make it idempotent: skip or update when the user already exists
  3. Check existence first via GET /users/{id} or an admin list before calling create
  4. Server-side: add UNIQUE constraints on username and email in the schema and catch sqlite3.IntegrityError to remove the race

Example fix

// before
    if cursor.fetchone()[0] > 0:
        raise ValueError("Username or email already exists")

// after (schema-backed, race-free)
    cursor.execute("CREATE UNIQUE INDEX IF NOT EXISTS ux_users_username ON users(username)")
    cursor.execute("CREATE UNIQUE INDEX IF NOT EXISTS ux_users_email ON users(email)")
    try:
        cursor.execute("INSERT INTO users (...) VALUES (...)")
    except sqlite3.IntegrityError:
        raise ValueError("Username or email already exists")
Defensive patterns

Strategy: validation

Validate before calling

def ensure_unique(user_manager, username: str, email: str) -> None:
    # mirrors the pre-check inside create_user to fail fast with a clear message
    existing = user_manager.get_user_by_username(username)
    if existing is not None:
        raise ValueError(f"username {username!r} taken")
    existing = user_manager.get_user_by_email(email)
    if existing is not None:
        raise ValueError(f"email {email!r} taken")

Try / catch

try:
    user = user_manager.create_user(user_data)
except ValueError as e:
    if "already exists" in str(e):
        user_data.username = f"{user_data.username}_{uuid4().hex[:6]}"
        user = user_manager.create_user(user_data)  # or surface to the user
    else:
        raise

Prevention

When it happens

Trigger: POST /auth/register with a username or email that already exists in the SQLite database (including the auto-created default 'admin'/'admin@n8n-workflows.com' account). A re-run of registration after a partial failure, or two concurrent registrations racing past the COUNT check, can also produce it (the race instead surfaces as an IntegrityError/500 if a constraint existed; here the check-then-insert is not transactional against concurrent writers).

Common situations: Scripts that re-seed users on every startup; users re-registering with a recycled email; the default admin account blocking a second 'admin'; concurrency races since SQLite connections are opened per call with no unique index backing the check.

Related errors


AI-assisted analysis of Zie619/n8n-workflows@94007c1445 (2026-08-15). Data as JSON: /api/errors/80fc861d3f69794d. Report an issue: GitHub.