spring-projects/spring-security · error · IllegalStateException

This has already been built with the following stacktrace. {

Error message

This has already been built with the following stacktrace. {buildToString}

What it means

ServerHttpSecurity.build() constructs the SecurityWebFilterChain and must run once per instance. A second call on the same ServerHttpSecurity instance throws IllegalStateException including the stacktrace of the first build to aid debugging.

Source

Thrown at config/src/main/java/org/springframework/security/config/web/server/ServerHttpSecurity.java:1079

	 * @param oneTimeTokenLoginCustomizer the {@link Customizer} to provide more options
	 * for the {@link OneTimeTokenLoginSpec}
	 * @return the {@link ServerHttpSecurity} for further customizations
	 */
	public ServerHttpSecurity oneTimeTokenLogin(Customizer<OneTimeTokenLoginSpec> oneTimeTokenLoginCustomizer) {
		if (this.oneTimeTokenLogin == null) {
			this.oneTimeTokenLogin = new OneTimeTokenLoginSpec();
		}
		oneTimeTokenLoginCustomizer.customize(this.oneTimeTokenLogin);
		return this;
	}

	/**
	 * Builds the {@link SecurityWebFilterChain}.
	 * @return the {@link SecurityWebFilterChain}
	 */
	public SecurityWebFilterChain build() {
		if (this.built != null) {
			throw new IllegalStateException(
					"This has already been built with the following stacktrace. " + buildToString());
		}
		this.built = new RuntimeException("First Build Invocation").fillInStackTrace();
		if (this.headers != null) {
			this.headers.configure(this);
		}
		WebFilter securityContextRepositoryWebFilter = securityContextRepositoryWebFilter();
		this.webFilters.add(securityContextRepositoryWebFilter);
		if (this.sessionManagement != null) {
			this.sessionManagement.configure(this);
		}
		if (this.httpsRedirectSpec != null) {
			this.httpsRedirectSpec.configure(this);
		}
		if (this.x509 != null) {
			this.x509.configure(this);
		}
		if (this.csrf != null) {

View on GitHub (pinned to 96852e8860)

Solutions

  1. Call build() exactly once per ServerHttpSecurity instance and cache/return the resulting SecurityWebFilterChain
  2. Restructure the configuration so each @Bean method creates its own HttpSecurity instance
  3. If reconfiguration is needed, create a new ServerHttpSecurity rather than rebuilding the old one

Example fix

// before
@Bean
SecurityWebFilterChain chain1(ServerHttpSecurity http) { return http.build(); }
@Bean
SecurityWebFilterChain chain2(ServerHttpSecurity http) { return http.build(); } // second build

// after
@Bean
SecurityWebFilterChain chain(ServerHttpSecurity http) { return http.build(); }
Defensive patterns

Strategy: try-catch

Validate before calling

// track build state yourself before calling build() a second time
if (alreadyBuilt) throw new IllegalStateException("ServerHttpSecurity already built");

Try / catch

try {
  return http.build();
} catch (IllegalStateException e) {
  if (e.getMessage().startsWith("This has already been built")) {
    // fix configuration: build() must be called once per ServerHttpSecurity
  }
  throw e;
}

Prevention

When it happens

Trigger: Calling build() (or SpringBootWebSecurityConfiguration/application code paths that call it) twice on the same ServerHttpSecurity instance; e.g. invoking build() inside a @Bean method and then again from framework wiring.

Common situations: Custom security configuration that both returns http.build() and triggers auto-configuration building the same instance; calling http.build() in a helper called from multiple places; programmatic filter chain creation reused across refreshes.

Understand the failure class

Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.

Related errors


AI-assisted analysis of spring-projects/spring-security@96852e8860 (2026-09-10). Data as JSON: /api/errors/574cdad7c0c39aa9. Report an issue: GitHub.