FlowiseAI/Flowise · error · Error

Error saving checkpoint

Error message

Error saving checkpoint

What it means

Thrown by SQLiteSaver.put() when the INSERT OR REPLACE upsert into the checkpoints table fails. Underlying error logged then re-thrown generically; finally destroys dataSource.

Source

Thrown at packages/components/nodes/memory/AgentMemory/SQLiteAgentMemory/sqliteSaver.ts:213

        if (!config.configurable?.checkpoint_id) return {}
        try {
            const queryRunner = dataSource.createQueryRunner()
            const row = [
                config.configurable?.thread_id || this.threadId,
                checkpoint.id,
                config.configurable?.checkpoint_id,
                this.serde.stringify(checkpoint),
                this.serde.stringify(metadata)
            ]
            const tableName = this.sanitizeTableName(this.tableName)
            const query = `INSERT OR REPLACE INTO ${tableName} (thread_id, checkpoint_id, parent_id, checkpoint, metadata) VALUES (?, ?, ?, ?, ?)`

            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()

View on GitHub (pinned to abe4a8601a)

Solutions

  1. Check console.error for the real SQLite error (SQLITE_BUSY, SQLITE_READONLY, SQL logic error, etc.).
  2. Recreate the checkpoints table to match current DDL.
  3. Enable WAL mode and raise busy_timeout via additionalConfig to mitigate write contention.
  4. Ensure the DB file/dir is writable.
  5. Maintainer: rethrow with cause.

Example fix

// before
} catch (error) {
    console.error('Error saving checkpoint', error)
    throw new Error('Error saving checkpoint')
}
// after
} catch (error) {
    throw new Error(`Error saving checkpoint: ${(error as Error).message}`, { cause: error })
}
Defensive patterns

Strategy: try-catch

Validate before calling

function assertCheckpointWritable(checkpoint: { id?: unknown }, config: { configurable?: { checkpoint_id?: unknown } }) {
  if (!config.configurable?.checkpoint_id) throw new Error('put() requires config.configurable.checkpoint_id')
  if (typeof checkpoint.id !== 'string' || !checkpoint.id) throw new Error('checkpoint.id must be a non-empty string')
}

Type guard

function isSqliteSaveError(e: unknown): boolean {
  return e instanceof Error && e.message === 'Error saving checkpoint'
}

Try / catch

try {
  await sqliteSaver.put(config, checkpoint, metadata)
} catch (e) {
  if (e instanceof Error && e.message === 'Error saving checkpoint') {
    // inspect log for SQLITE_BUSY/READONLY; enable WAL; verify schema BLOB columns
  }
  throw e
}

Prevention

When it happens

Trigger: put() with checkpoint_id present while the table is missing, schema drifted (non-BLOB columns), payload too large, file locked, or role/FS permissions issue.

Common situations: Schema drift after upgrade. SQLite busy/locked under concurrent writes. Filesystem read-only. Very large serialized checkpoints.

Related errors


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