apache/maven · error · DIException

Cannot access {} outside of a scoping block

Error message

Cannot access {} outside of a scoping block

What it means

MojoExecutionScope.scope(key, unscoped) returns a supplier that first checks the thread-local stack of ScopeState created by enter(). If no scoping block is active (stack null or empty — enter() never called on this thread, or exit() already ran), resolving any mojo-scoped key throws DIException("Cannot access ... outside of a scoping block"). Mojo-scoped objects exist only inside Maven's mojo execution window.

Source

Thrown at impl/maven-impl/src/main/java/org/apache/maven/impl/di/MojoExecutionScope.java:105

        }
    }

    public <T> void seed(Class<T> clazz, Supplier<T> value) {
        getScopeState().seed(clazz, value);
    }

    public <T> void seed(Class<T> clazz, final T value) {
        seed(clazz, (Supplier<T>) () -> value);
    }

    @SuppressWarnings("unchecked")
    @Nonnull
    @Override
    public <T> Supplier<T> scope(@Nonnull Key<T> key, @Nonnull Supplier<T> unscoped) {
        return () -> {
            LinkedList<ScopeState> stack = values.get();
            if (stack == null || stack.isEmpty()) {
                throw new DIException("Cannot access " + key + " outside of a scoping block");
            }

            ScopeState state = stack.getFirst();

            Supplier<?> seeded = state.seeded.get(key);

            if (seeded != null) {
                return (T) seeded.get();
            }

            T provided = (T) state.provided.get(key);
            if (provided == null && unscoped != null) {
                provided = unscoped.get();
                state.provided.put(key, provided);
            }

            return provided;
        };

View on GitHub (pinned to e4093d4e12)

Solutions

  1. Perform all lookups of mojo-scoped objects on the execution thread, inside the scoped block
  2. In tests, bracket the code: scope.enter(); try { ... } finally { scope.exit(); }
  3. Capture the needed instances as plain fields before going asynchronous instead of resolving scoped keys off-thread

Example fix

// before: resolving mojo-scoped bean off-thread
executor.submit(() -> injector.getInstance(MyComponent.class)); // DIException

// after: capture inside the scope, use plain object off-thread
MyComponent c = injector.getInstance(MyComponent.class); // on execution thread
executor.submit(() -> c.doWork());
Defensive patterns

Strategy: validation

Validate before calling

// structure code so scoped resolution happens inside the scoping block
mojoExecutionScope.enter();
try {
    MyComponent c = injector.getInstance(MyComponent.class); // scope active on this thread
    doWork(c);
} finally {
    mojoExecutionScope.exit();
}

Try / catch

try {
    return injector.getInstance(MyComponent.class);
} catch (DIException e) {
    if (e.getMessage() != null && e.getMessage().contains("outside of a scoping block")) {
        // re-dispatch the work onto the execution thread inside the scope
        return callInsideMojoScope(() -> injector.getInstance(MyComponent.class));
    }
    throw e;
}

Prevention

When it happens

Trigger: Resolving a MojoExecutionScope-scoped binding from a different thread than the one that called enter(); resolving after exit() has popped the state; looking up mojo-scoped types during plugin metadata parsing rather than execution.

Common situations: Parallel builds moving work onto executor threads; asynchronous code (CompletableFuture, custom thread pools) touching scoped beans; tests forgetting the enter()/exit() bracket; lazy suppliers evaluated after the mojo finished.

Related errors


AI-assisted analysis of apache/maven@e4093d4e12 (2026-08-21). Data as JSON: /api/errors/752ab2547d1deaf0. Report an issue: GitHub.