From f7e4f81773b2b2292ff866e545ccbac52d8dee23 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 19 Mar 2026 18:51:55 +0000 Subject: [PATCH 1/2] Initial plan From 689c616e448bf3edfcb91b347ae9f6fac43b1983 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 19 Mar 2026 18:58:19 +0000 Subject: [PATCH 2/2] fix(ui): show proper error when signup username has invalid characters When a username like 'christianlouis.de' (containing a dot) was submitted on the signup page, FastAPI returned a 422 with detail as an array of Pydantic validation error objects. The JS code assigned that array directly to `this.error`, causing Alpine.js x-text to render '[object Object]'. Two fixes applied in signup.html: 1. Client-side validation: check username length and pattern in submit() before the API call, with clear human-readable error messages. 2. Server error handling: detect when data.detail is an Array and extract each entry's .msg field, joining them into a readable string. Also adds a regression test to confirm the 422 response format for an invalid username (with dot) includes a list detail with msg fields. Co-authored-by: christianlouis <361235+christianlouis@users.noreply.github.com> --- frontend/templates/signup.html | 15 ++++++++++++++- tests/test_local_auth.py | 29 +++++++++++++++++++++++++++++ 2 files changed, 43 insertions(+), 1 deletion(-) diff --git a/frontend/templates/signup.html b/frontend/templates/signup.html index 1cb3a043..6f83d664 100644 --- a/frontend/templates/signup.html +++ b/frontend/templates/signup.html @@ -32,6 +32,14 @@ error: '', async submit() { this.error = ''; + if (this.username.length < 3 || this.username.length > 64) { + this.error = 'Username must be between 3 and 64 characters.'; + return; + } + if (!/^[a-zA-Z0-9_-]+$/.test(this.username)) { + this.error = 'Username may only contain letters, numbers, hyphens, and underscores. Dots and other special characters are not allowed.'; + return; + } if (this.password !== this.password_confirm) { this.error = 'Passwords do not match.'; return; @@ -58,7 +66,12 @@ } } else { const data = await resp.json(); - this.error = data.detail || 'Registration failed. Please try again.'; + const detail = data.detail; + if (Array.isArray(detail)) { + this.error = detail.map(e => e.msg || String(e)).join(' ') || 'Registration failed. Please try again.'; + } else { + this.error = detail || 'Registration failed. Please try again.'; + } } } catch(e) { this.error = 'Network error. Please try again.'; diff --git a/tests/test_local_auth.py b/tests/test_local_auth.py index 8a53604c..babbbc30 100644 --- a/tests/test_local_auth.py +++ b/tests/test_local_auth.py @@ -322,6 +322,35 @@ def test_signup_duplicate_username(la_client, active_user): assert "Username" in resp.json()["detail"] +@pytest.mark.integration +def test_signup_invalid_username_with_dot(la_client): + """POST /api/auth/signup returns 422 with a list detail when username contains a dot. + + This is a regression test for the bug where ``data.detail`` was an array, + causing the frontend to display ``[object Object]`` instead of a message. + """ + with patch("app.api.local_auth.settings") as mock_settings: + mock_settings.allow_local_signup = True + mock_settings.multi_user_enabled = True + mock_settings.email_host = "smtp.example.com" + resp = la_client.post( + "/api/auth/signup", + json={ + "email": "a@example.com", + "username": "christian.louis", + "password": "password1", + "password_confirm": "password1", + }, + ) + assert resp.status_code == 422 + detail = resp.json()["detail"] + # FastAPI returns a list of validation errors for Pydantic constraint failures. + # Each entry must be a dict with a "msg" key so the frontend can extract a readable message. + assert isinstance(detail, list), "detail should be a list for Pydantic validation errors" + assert len(detail) > 0 + assert "msg" in detail[0] + + @pytest.mark.integration def test_signup_smtp_failure_cleans_up(la_client, la_session): """POST /api/auth/signup cleans up user records if email send fails."""