pestphp/pest · error · AfterAllWithinDescribe

The [afterAll] hook may not be used inside a [describe] bloc

Error message

The [afterAll] hook may not be used inside a [describe] block. Please move it to the top level of [%s].

What it means

Symmetric to beforeAll: afterAll() registers a once-per-file teardown hook on the TestSuite, and calling it inside a describe() block throws AfterAllWithinDescribe with the file name. Suite-level All-hooks must live at the top level of the test file; each-variants are unrestricted.

Source

Thrown at src/Functions.php:161

if (! function_exists('afterEach')) {
    /**
     * @param-closure-this TestCall  $closure
     */
    function afterEach(?Closure $closure = null): AfterEachCall
    {
        $filename = Backtrace::testFile();

        return new AfterEachCall(TestSuite::getInstance(), $filename, $closure);
    }
}

if (! function_exists('afterAll')) {
    function afterAll(Closure $closure): void
    {
        if (DescribeCall::describing() !== []) {
            $filename = Backtrace::testFile();

            throw new AfterAllWithinDescribe($filename);
        }

        TestSuite::getInstance()->afterAll->set($closure);
    }
}

if (! function_exists('covers')) {
    /**
     * @param  array<int, string>|string  $classesOrFunctions
     */
    function covers(array|string ...$classesOrFunctions): void
    {
        $filename = Backtrace::testFile();

        $beforeEachCall = (new BeforeEachCall(TestSuite::getInstance(), $filename));

        $beforeEachCall->covers(...$classesOrFunctions);
        $beforeEachCall->group('__pest_mutate_only');

View on GitHub (pinned to 1af74a215c)

Solutions

  1. Move the afterAll() call to the top level of the test file.
  2. Use afterEach() inside the describe for group-scoped teardown.
  3. Split the describe into its own file if it truly needs file-scoped teardown independent of other groups.

Example fix

// before
describe('imports', function () {
    afterAll(fn () => cleanupStorage()); // throws
    it('imports csv', fn () => ...);
});
// after
afterAll(fn () => cleanupStorage());
describe('imports', function () {
    it('imports csv', fn () => ...);
});
Defensive patterns

Strategy: validation

Prevention

When it happens

Trigger: Calling afterAll(function () { ... }) inside describe('...', function () { ... }); refactoring teardown into groups; copy-pasting a describe body that contained hooks from a Jest-style project.

Common situations: Grouping tests late in a file's life and moving hooks with them; migrating from Jest/Jasmine where afterAll inside describe is standard; attempting teardown scoped to one feature group.

Related errors


AI-assisted analysis of pestphp/pest@1af74a215c (2026-08-21). Data as JSON: /api/errors/311efec487881850. Report an issue: GitHub.