laurent22/joplin · error · ErrorForbidden
Signup is not enabled
Error message
Signup is not enabled
What it means
The POST signup route throws ErrorForbidden('Signup is not enabled') when config().signupEnabled is false. The Joplin Server administrator must explicitly enable self-service account creation; otherwise all signup attempts are rejected with 403.
Solutions
- Enable signup in the server config (set SIGNUP_ENABLED or the equivalent config value to true) and restart the server.
- Ask the server administrator to create the account via the admin user-management route instead.
- Check that you are posting to the correct server whose config you updated.
Example fix
// before (docker-compose / env) # signup not configured // after environment: - Signup_Enabled=true
Defensive patterns
Strategy: try-catch
Try / catch
try { await api.post('/signup', form); } catch (e) { if (e.httpStatus === 403) showMessage('Signup is disabled on this server'); } Prevention
- Query server config/capabilities before showing the signup form.
- Admins: set Signup_Enabled explicitly when self-registration is desired.
When it happens
Trigger: POSTing registration form data (email/password/repeat password) to the signup endpoint on a server where signupEnabled is not set to true in the config.
Common situations: Self-hosted Joplin Server default configuration where signup is disabled; users sharing a server URL expecting open registration; after a config migration that dropped the signupEnabled flag.
Related errors
- AWS S3 bucket not found
- Could not access data on server
- email must be set
- Full name must be at most 256 characters
- No URL for SAML authentication set.
AI-assisted analysis of laurent22/joplin@981a03c5c9 (2026-09-17).
Data as JSON: /api/errors/46f6eef4165ce56b.
Report an issue: GitHub.
Appendix: source
Thrown at packages/server/src/routes/index/signup.ts:41
}
export interface FormUser {
full_name: string;
email: string;
password: string;
password2: string;
}
const router: Router = new Router(RouteType.Web);
router.public = true;
router.get('signup', async (_path: SubPath, _ctx: AppContext) => {
return makeView();
});
router.post('signup', async (_path: SubPath, ctx: AppContext) => {
if (!config().signupEnabled) throw new ErrorForbidden('Signup is not enabled');
await limiterSignupBruteForce(userIp(ctx));
try {
const formUser = await bodyFields<FormUser>(ctx.req);
const password = checkRepeatPassword(formUser, true);
const user = await ctx.joplin.models.user().save({
account_type: AccountType.Basic,
email: formUser.email,
full_name: formUser.full_name,
password,
});
const session = await ctx.joplin.models.session().createUserSession(user.id);
cookieSet(ctx, 'sessionId', session.id);
return redirect(ctx, `${config().baseUrl}/home`);View on GitHub (pinned to 981a03c5c9)