TryGhost/Ghost · error · Error

Cannot create without a persistence adapter. Use buildMany()

Error message

Cannot create without a persistence adapter. Use buildMany() for in-memory objects.

What it means

Thrown by Factory.createMany() when the factory was constructed without a PersistenceAdapter. Factories support two modes: build()/buildMany() produce in-memory objects (no adapter needed), while create()/createMany() persist through an adapter. Calling createMany() without an adapter is a programming error — the factory cannot know where to insert. The error message points you to the correct in-memory alternative.

Source

Thrown at e2e/data-factory/factory.ts:28

    }

    abstract build(options?: Partial<TOptions>): TResult;

    buildMany(optionsList: Partial<TOptions>[]): TResult[] {
        return optionsList.map(options => this.build(options));
    }

    async create(options?: Partial<TOptions>): Promise<TResult> {
        if (!this.adapter) {
            throw new Error('Cannot create without a persistence adapter. Use build() for in-memory objects.');
        }
        const data = this.build(options);
        return await this.adapter.insert(this.entityType, data) as Promise<TResult>;
    }

    async createMany(optionsList: Partial<TOptions>[]): Promise<TResult[]> {
        if (!this.adapter) {
            throw new Error('Cannot create without a persistence adapter. Use buildMany() for in-memory objects.');
        }

        const results: TResult[] = [];
        for (const options of optionsList) {
            const result = await this.create(options);
            results.push(result);
        }
        return results;
    }
}

View on GitHub (pinned to 47d8b0e2ad)

Solutions

  1. If you want persisted entities, pass an adapter when constructing the factory (e.g. new ApiPersistenceAdapter({httpClient: page.request, endpoint: '/api/admin/posts/'})).
  2. If you only need in-memory objects, call buildMany(optionsList) instead of createMany(optionsList).
  3. If the adapter is conditionally available, guard the createMany call behind an 'if (factory has adapter)' check.
  4. Run 'pnpm build' after factory changes (per the e2e AGENTS.md) to catch wiring issues.

Example fix

// before: no adapter, persistence impossible
const factory = new PostFactory();
const posts = await factory.createMany([{status: 'published'}, {status: 'draft'}]);

// after: wire an adapter for persistence
const adapter = new ApiPersistenceAdapter({httpClient: page.request, endpoint: `${baseURL}/api/admin/posts/`});
const factory = new PostFactory(adapter);
const posts = await factory.createMany([{status: 'published'}, {status: 'draft'}]);

// or, for in-memory only, use buildMany:
const factory = new PostFactory();
const posts = factory.buildMany([{status: 'published'}, {status: 'draft'}]);
Defensive patterns

Strategy: validation

Validate before calling

import {Factory} from './factory';

function assertAdapter(factory) {
    if (!factory.adapter) {
        throw new Error(`${factory.constructor.name} has no persistence adapter. Pass one to the constructor, or use buildMany() for in-memory objects.`);
    }
}

// Before calling createMany:
assertAdapter(factory);
await factory.createMany(list);

Type guard

function hasAdapter(factory) {
    return factory instanceof Factory && factory.adapter != null;
}

Prevention

When it happens

Trigger: Test code instantiates a factory via 'new MyFactory()' (no adapter) and then calls factory.createMany([...]) expecting persistence. The base Factory class guards both create() and createMany() with this check; createMany() specifically tells you to use buildMany().

Common situations: Copied a factory usage pattern but forgot to wire the adapter (ApiPersistenceAdapter or KnexPersistenceAdapter); refactored a test from persisted to in-memory data but left a createMany() call; factory was constructed with a null/undefined adapter argument.

Related errors


AI-assisted analysis of TryGhost/Ghost@47d8b0e2ad (2026-08-13). Data as JSON: /api/errors/24e2feba61b96879. Report an issue: GitHub.