FlowiseAI/Flowise · error · Error

Error saving checkpoint

Error message

Error saving checkpoint

What it means

MySQLSaver.put wraps the INSERT ... ON DUPLICATE KEY UPDATE; any DB or serde.stringify error is logged via console.error and re-thrown generically. The connection is destroyed in finally.

Source

Thrown at packages/components/nodes/memory/AgentMemory/MySQLAgentMemory/mysqlSaver.ts:191

            const queryRunner = dataSource.createQueryRunner()
            const row = [
                config.configurable?.thread_id || this.threadId,
                checkpoint.id,
                config.configurable?.checkpoint_id,
                Buffer.from(this.serde.stringify(checkpoint)), // Encode to binary
                Buffer.from(this.serde.stringify(metadata)) // Encode to binary
            ]
            const tableName = this.sanitizeTableName(this.tableName)

            const query = `INSERT INTO ${tableName} (thread_id, checkpoint_id, parent_id, checkpoint, metadata)
                           VALUES (?, ?, ?, ?, ?)
                           ON DUPLICATE KEY UPDATE checkpoint = VALUES(checkpoint), metadata = VALUES(metadata)`

            await queryRunner.manager.query(query, row)
            await queryRunner.release()
        } catch (error) {
            console.error('Error saving checkpoint', error)
            throw new Error('Error saving checkpoint')
        } finally {
            await dataSource.destroy()
        }

        return {
            configurable: {
                thread_id: config.configurable?.thread_id || this.threadId,
                checkpoint_id: checkpoint.id
            }
        }
    }

    async delete(threadId: string): Promise<void> {
        if (!threadId) return

        const dataSource = await this.getDataSource()
        await this.setup(dataSource)
        const tableName = this.sanitizeTableName(this.tableName)

View on GitHub (pinned to abe4a8601a)

Solutions

  1. Check console logs to distinguish a serde failure from a DB failure.
  2. Raise max_allowed_packet on the MySQL server if payloads are large.
  3. Ensure checkpoint/metadata are serializable (no functions, circular refs, BigInt).
  4. Verify connection stability and INSERT/UPDATE privileges.
Defensive patterns

Strategy: try-catch

Validate before calling

// Pre-serialise to fail before hitting the DB on non-serializable state
function assertSerializable(value: unknown): void {
  structuredClone(value) // throws on non-cloneable (functions, etc.)
  JSON.stringify(value)   // throws on circular refs
}

Try / catch

try {
  await saver.put(config, checkpoint, metadata)
} catch (e) {
  if (/Error saving checkpoint/.test((e as Error).message)) {
    // inspect logs: serde vs DB; raise max_allowed_packet if payload too large
  }
  throw e
}

Prevention

When it happens

Trigger: checkpoint/metadata payload too large for the BLOB / max_allowed_packet; serde.stringify throws on a non-serializable checkpoint; connection lost mid-write; PK conflict edge cases.

Common situations: Very large graph state exceeding MySQL packet size; non-serializable value in checkpoint metadata; flaky connection during a write; concurrent writes racing on the same checkpoint_id.

Related errors


AI-assisted analysis of FlowiseAI/Flowise@abe4a8601a (2026-08-12). Data as JSON: /api/errors/9014eef64ebf6865. Report an issue: GitHub.