spring-projects/spring-security · error · AlreadyBuiltException

This object has already been built

Error message

This object has already been built

What it means

AbstractSecurityBuilder.build() uses an AtomicBoolean 'building' flag with compareAndSet so the object can only be built once; a second build() call throws AlreadyBuiltException('This object has already been built'). Builders are deliberately single-use to guarantee a consistent, immutable built object.

Source

Thrown at config/src/main/java/org/springframework/security/config/annotation/AbstractSecurityBuilder.java:41

 * time.
 *
 * @param <O> the type of Object that is being built
 * @author Rob Winch
 *
 */
public abstract class AbstractSecurityBuilder<O> implements SecurityBuilder<O> {

	private AtomicBoolean building = new AtomicBoolean();

	private O object;

	@Override
	public final O build() {
		if (this.building.compareAndSet(false, true)) {
			this.object = doBuild();
			return this.object;
		}
		throw new AlreadyBuiltException("This object has already been built");
	}

	/**
	 * Gets the object that was built. If it has not been built yet an Exception is
	 * thrown.
	 * @return the Object that was built
	 */
	public final O getObject() {
		if (!this.building.get()) {
			throw new IllegalStateException("This object has not been built");
		}
		return this.object;
	}

	/**
	 * Subclasses should implement this to perform the build.
	 * @return the object that should be returned by {@link SecurityBuilder#build()}.
	 * @throws Exception if an error occurs

View on GitHub (pinned to 96852e8860)

Solutions

  1. Call build() only once per builder instance; create a new builder for each build cycle.
  2. Use getObject() to retrieve the already-built object instead of building again.
  3. Store the built result yourself (e.g. in a field) and reuse it rather than re-triggering build().
  4. In tests, construct builders in setup (@BeforeEach) so each test gets a fresh instance.

Example fix

// before
manager = builder.build();
// later, same builder
other = builder.build(); // AlreadyBuiltException
// after
manager = builder.build();
other = manager; // reuse built object, or new SomeBuilder() for a fresh build
Defensive patterns

Strategy: type-guard

Validate before calling

if (builder instanceof AbstractSecurityBuilder<?> b && b.getObject() != null) {
    // already built; do not call build() again
}

Try / catch

try {
    object = builder.build();
} catch (AlreadyBuiltException e) {
    object = builder.getObject(); // reuse previously built instance
}

Prevention

When it happens

Trigger: Invoking build() a second time on the same builder (e.g. AuthenticationManagerBuilder, HttpSecurity, ProviderManager builders), typically by calling build() manually and then letting the framework build again, or building in a loop/refresh without recreating the builder.

Common situations: Calling securityContextBuilder.build() inside @PostConstruct and again from another bean; Spring context refresh re-invoking configuration on a cached builder; tests reusing a static builder across test cases.

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/b1108e9096bf69bb. Report an issue: GitHub.