apify/crawlee · error · Error

The state conversion for '${persistStateKey}' validated asyn

Error message

The state conversion for '${persistStateKey}' validated asynchronously, which this caller cannot await.

What it means

RecoverableState validates its persisted state through a Standard Schema converter, but convertStateSync only supports validators whose validate() returns results synchronously. If the schema's validate() returned a Promise (detected via 'then' in result), the synchronous callers (#resolveDefaultStateExtension, #restoreStateExtension, #serializeStateExtension) cannot await it, so it throws rather than silently skipping validation.

Source

Thrown at packages/core/src/recoverable_state.ts:46

/**
 * Applies a {@apilink SyncStateConversion}, throwing a {@apilink StateValidationError} for a schema that rejects
 * the value.
 *
 * @internal
 */
export function convertStateSync<TFrom, TTo>(
    conversion: SyncStateConversion<TFrom, TTo>,
    value: TFrom,
    persistStateKey: string,
): TTo {
    if (typeof conversion === 'function') {
        return conversion(value);
    }

    const result = conversion['~standard'].validate(value);

    if ('then' in result) {
        throw new Error(
            `The state conversion for '${persistStateKey}' validated asynchronously, which this caller cannot await.`,
        );
    }

    if (result.issues) {
        throw new StateValidationError(persistStateKey, result.issues);
    }

    return result.value;
}

export interface RecoverableStatePersistenceOptions {
    /**
     * The key under which the state is stored in the KeyValueStore
     */
    persistStateKey: string;

    /**

View on GitHub (pinned to dbe57fb09c)

Solutions

  1. Replace the schema with a purely synchronous one (no async refinements/transforms) for the given persistStateKey.
  2. Remove async validation logic (e.g. z.string().refine(async ...) or async superRefinement) from the state schema.
  3. Pre-validate the value synchronously before handing it to RecoverableState.
  4. If async validation is unavoidable, perform it yourself before/around state restore instead of relying on the sync converter.

Example fix

// before
const schema = z.object({ at: z.string() }).refine(async (v) => checkRemote(v)); // async validate
// after
const schema = z.object({ at: z.string() }).refine((v) => !Number.isNaN(Date.parse(v.at))); // sync validate
Defensive patterns

Strategy: validation

Validate before calling

// Ensure the schema validates synchronously before handing it to RecoverableState
const result = schema['~standard'].validate(value);
if (result instanceof Promise || 'then' in (result as any)) {
  throw new Error('State schema must validate synchronously (no async refinements)');
}

Type guard

function isSyncStandardSchema<T>(s: unknown): s is StandardSchemaV1<T> {
  try {
    const r = (s as any)['~standard'].validate(undefined);
    return !(r instanceof Promise) && !('then' in r);
  } catch { return false; }
}

Try / catch

try {
  await state.initialize();
} catch (err) {
  if ((err as Error).message.includes('validated asynchronously')) {
    throw new Error(`Schema for '${persistStateKey}' uses async validation; make it synchronous`);
  }
  throw err;
}

Prevention

When it happens

Trigger: Providing a conversion/schema (e.g. an async Standard Schema implementation or a zod schema behind an async adapter) whose `~standard.validate` returns a thenable, then loading/serializing the RecoverableState (initialize/serialize paths).

Common situations: Using a schema library configured for async validation (async refinements/transforms); wrapping the converter so validate returns a Promise; switching schema implementations while keeping the sync recovery path.

Related errors


AI-assisted analysis of apify/crawlee@dbe57fb09c (2026-08-30). Data as JSON: /api/errors/d3738450b4a178a2. Report an issue: GitHub.