dromara/Sa-Token · error · SaOAuth2Exception

30104

30104

Error message

LoginId 不可为空

What it means

Thrown by RequestAuthModel.checkModel() when loginId is empty. loginId identifies the user on whose behalf the token is issued; note the check uses String.valueOf(loginId), so a literal empty-string loginId triggers it while a null loginId becomes the string "null" and passes this particular check. Error code 30104.

Source

Thrown at sa-token-plugin/sa-token-oauth2/src/main/java/cn/dev33/satoken/oauth2/data/model/request/RequestAuthModel.java:198

		return this;
	}

	/**
	 * 数据自检
	 * @return 对象自身
	 */
	public RequestAuthModel checkModel() {
		if(SaFoxUtil.isEmpty(clientId)) {
			throw new SaOAuth2Exception("client_id 不可为空").setCode(SaOAuth2ErrorCode.CODE_30101);
		}
		if(SaFoxUtil.isEmpty(scopes)) {
			throw new SaOAuth2Exception("scope 不可为空").setCode(SaOAuth2ErrorCode.CODE_30102);
		}
		if(SaFoxUtil.isEmpty(redirectUri)) {
			throw new SaOAuth2Exception("redirect_uri 不可为空").setCode(SaOAuth2ErrorCode.CODE_30103);
		}
		if(SaFoxUtil.isEmpty(String.valueOf(loginId))) {
			throw new SaOAuth2Exception("LoginId 不可为空").setCode(SaOAuth2ErrorCode.CODE_30104);
		}
		return this;
	}

	@Override
	public String toString() {
		return "RequestAuthModel{" +
				"clientId='" + clientId + '\'' +
				", scopes=" + scopes +
				", loginId=" + loginId +
				", redirectUri='" + redirectUri + '\'' +
				", responseType='" + responseType + '\'' +
				", state='" + state + '\'' +
				", nonce='" + nonce + '\'' +
				'}';
	}

}

View on GitHub (pinned to ac2c7f6e94)

Solutions

  1. Ensure the user is logged in before the authorize/confirm step (check StpUtil.isLogin() and redirect to login if not)
  2. Verify StpUtil.login(userId) was actually called in your login endpoint
  3. If building RequestAuthModel manually, always assign a non-empty loginId from your auth result

Example fix

// before
Object loginId = StpUtil.getLoginIdDefaultNull(); // may be null/empty after session expiry
ra.loginId = loginId;

// after
if(!StpUtil.isLogin()) { return res.redirect("/login?back=" + currUrl); }
ra.loginId = StpUtil.getLoginId();
Defensive patterns

Strategy: validation

Validate before calling

Object loginId = StpUtil.getLoginIdDefaultNull();
if(loginId == null || "".equals(String.valueOf(loginId))) {
    // force re-login instead of proceeding
    return res.redirect(loginPage + "?back=" + currUrl);
}

Try / catch

catch(SaOAuth2Exception e) { if("30104".equals(e.getCode())) redirect to login; }

Prevention

When it happens

Trigger: Authorize flow reached without a logged-in session, so the loginId put into RequestAuthModel is an empty string; custom code that sets ra.loginId = "" before checkModel().

Common situations: User's session expired or was kicked before confirming authorization; StpLogic.getLoginId() returned an empty value that was copied into the model; login callback did not actually perform StpUtil.login().

Related errors


AI-assisted analysis of dromara/Sa-Token@ac2c7f6e94 (2026-08-14). Data as JSON: /api/errors/33921d7551ad8177. Report an issue: GitHub.