-
Notifications
You must be signed in to change notification settings - Fork 1.8k
Description
Describe the bug
When signing up with password (using the "Set Password Now" toggle on the signup page), the UI shows:
Unknown error: Unexpected token 'N', "Not Found" is not valid JSON
...or:
Unknown error: Unexpected token '<', "<html> <h"... is not valid JSON
The account IS created successfully, but the user is stuck on the signup page with the error. They must navigate to Sign In and use OTP to log in.
Root cause
The signUp endpoint in server/account/src/operations.ts intentionally returns token: undefined when MAIL_URL is configured (production deployments with email confirmation):
return {
account,
name: getPersonName(person),
socialId,
token: !forceConfirmation ? generateToken(account) : undefined // undefined in production
}However, SignupForm.svelte calls logIn(result) unconditionally without checking if result.token is defined:
// plugins/login-resources/src/components/SignupForm.svelte
const [loginStatus, result] = await signUp(object.username, object.password, object.first, object.last)
status = loginStatus
if (result != null) {
await logIn(result) // BUG: calls logIn even when result.token is undefined
goTo('confirmationSend')
}This triggers setCookie() which sends PUT /cookie without an Authorization header. The account service returns 401 with an empty/invalid body, and the client crashes trying to parse it as JSON.
The OTP signup flow handles this correctly. doLoginNavigate() in plugins/login-resources/src/utils.ts has the proper guard:
if (result.token != null) { // Correctly checks for token
await logIn(result)
}
loc.path[1] = result.token != null ? 'selectWorkspace' : 'confirmationSend'Regression
Introduced in PR #8057 ("feat: cookie token", 2025-02-24). Before that PR, the password signup path used setMetadata() which tolerated undefined tokens. The PR changed it to await logIn(result) → setCookie() which requires a valid token.
Secondary issue
The PUT /cookie endpoint in server/account-service/src/index.ts uses raw Node.js response handling when returning 401:
ctx.body = JSON.stringify({ error: new Status(...) })
ctx.res.writeHead(401)
ctx.res.end() // Bypasses Koa — ctx.body may not be sentThis can produce an empty response body or cause "upstream prematurely closed connection" errors in reverse proxies. The client then fails at response.json() because the body isn't valid JSON.
Steps to reproduce
- Deploy Huly with
MAIL_URLconfigured (any SMTP/SES setup) - Go to the signup page
- Toggle "Set Password Now" (or use the password signup form)
- Enter first name, last name, email, and password
- Click "Sign Up"
- Error appears: "Unknown error: Unexpected token..."
Expected behavior
After password signup with email confirmation required:
- Account should be created (it is)
- User should be redirected to "confirmation sent" page (not shown — error appears instead)
- User checks email for confirmation/OTP and completes login
Suggested fix
Add the token != null guard in SignupForm.svelte, matching the OTP flow:
if (result != null) {
if (result.token != null) {
await logIn(result)
}
goTo('confirmationSend')
}And fix the PUT /cookie 401 response to use Koa's response mechanism instead of raw ctx.res:
ctx.status = 401
ctx.body = { error: new Status(Severity.ERROR, platform.status.Unauthorized, {}) }Environment
- Huly version: v0.7.353 (
hardcoreeng/*Docker images) - Deployment: Self-hosted via Docker Compose with CockroachDB
- Reverse proxy: nginx (Dokploy)
- SMTP: Configured and working (OTP emails deliver successfully)
Workaround
The account IS created despite the error. Users can:
- Dismiss the error
- Go to Sign In
- Enter email/password
- Complete OTP verification via email
- Login succeeds normally