apify/crawlee · error · StateValidationError

StateValidationError(this.#persistStateKey, result.issues)

Error message

StateValidationError(this.#persistStateKey, result.issues)

What it means

#toConversion wraps a schema into an async validator that runs '~standard'.validate and throws StateValidationError (keyed by the persistence key) when the value has validation issues. It is invoked from the RecoverableState constructor, so constructing a RecoverableState with a value that fails its schema throws immediately.

Source

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

                this.#log.warning(`Failed to persist the state under key '${this.#persistStateKey}'.`, { error }),
            );
    }

    /** Normalizes a conversion option into a function. Absent conversions pass the value through unchanged. */
    #toConversion<TFrom, TTo>(conversion: StateConversion<TFrom, TTo> | undefined): (value: TFrom) => Promise<TTo> {
        if (conversion === undefined) {
            return async (value) => value as unknown as TTo;
        }

        if (typeof conversion === 'function') {
            return async (value) => conversion(value);
        }

        return async (value) => {
            const result = await conversion['~standard'].validate(value);

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

            return result.value;
        };
    }

    /**
     * Initialize the recoverable state.
     *
     * If persistence is enabled, this method loads the saved state and registers the object to listen for
     * PERSIST_STATE events. A state established beforehand by {@apilink RecoverableState.reset} survives if there
     * is no record to restore.
     *
     * Calling this again after a {@apilink RecoverableState.teardown} starts a new persistence window - the
     * listener is registered again and the record reloaded.
     *
     * @returns The loaded state object
     */

View on GitHub (pinned to dbe57fb09c)

Solutions

  1. Read the validation issues attached to the error and fix the initial value accordingly
  2. Validate defaults with the schema before constructing RecoverableState
  3. Ensure the schema passed to the constructor matches the value's actual type
  4. Add schema defaults for newly required fields so legacy callers still construct

Example fix

// before
new RecoverableState({ key: 'cart', schema: cartSchema, value: { items } });
// after
const value = cartSchema.parse({ items, updatedAt: Date.now() });
new RecoverableState({ key: 'cart', schema: cartSchema, value });
Defensive patterns

Strategy: validation

Validate before calling

const parsed = schema['~standard'].validate(initialValue);
if ((await parsed).issues) console.warn('initialValue fails schema', (await parsed).issues);

Type guard

function isValidState(v): v is TState { return !schema['~standard'].validate(v).issues; }

Try / catch

try {
  const rs = new RecoverableState({ key, schema, value });
} catch (err) {
  if (err instanceof StateValidationError) {
    return new RecoverableState({ key, schema, value: schemaDefaults });
  }
  throw err;
}

Prevention

When it happens

Trigger: new RecoverableState(...) where the initialValue (or value passed to the returned conversion) does not satisfy the provided schema.

Common situations: Passing a partially-built default state object that misses required fields; supplying the wrong schema for the value type; refactoring the state model without updating construction call sites.

Understand the failure class

Background: Schema validation failed / invalid input schema: payload rejected because its shape doesn't match the expected schema — this error's family across 28 libraries.

Related errors


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