{"record":{"id":"a71f8388f420cfff","repo":"FlowiseAI/Flowise","slug":"time-sync-issue-with-database-updatedat-tim-a71f83","errorCode":null,"errorMessage":"Time sync issue with database ${updatedAt} < ${timeAtLeast}","messagePattern":"Time sync issue with database (.+?) < (.+?)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/components/nodes/recordmanager/SQLiteRecordManager/SQLiteRecordManager.ts","lineNumber":251,"sourceCode":"            throw error\n        } finally {\n            await dataSource.destroy()\n        }\n    }\n\n    async update(keys: Array<{ uid: string; docId: string }> | string[], updateOptions?: UpdateOptions): Promise<void> {\n        if (keys.length === 0) {\n            return\n        }\n        const dataSource = await this.getDataSource()\n        const queryRunner = dataSource.createQueryRunner()\n        const tableName = this.sanitizeTableName(this.tableName)\n\n        const updatedAt = await this.getTime()\n        const { timeAtLeast, groupIds: _groupIds } = updateOptions ?? {}\n\n        if (timeAtLeast && updatedAt < timeAtLeast) {\n            throw new Error(`Time sync issue with database ${updatedAt} < ${timeAtLeast}`)\n        }\n\n        // Handle both new format (objects with uid and docId) and old format (strings)\n        const isNewFormat = keys.length > 0 && typeof keys[0] === 'object' && 'uid' in keys[0]\n        const keyStrings = isNewFormat ? (keys as Array<{ uid: string; docId: string }>).map((k) => k.uid) : (keys as string[])\n        const docIds = isNewFormat ? (keys as Array<{ uid: string; docId: string }>).map((k) => k.docId) : keys.map(() => null)\n\n        const groupIds = _groupIds ?? keyStrings.map(() => null)\n\n        if (groupIds.length !== keyStrings.length) {\n            throw new Error(`Number of keys (${keyStrings.length}) does not match number of group_ids (${groupIds.length})`)\n        }\n\n        const recordsToUpsert = keyStrings.map((key, i) => [key, this.namespace, updatedAt, groupIds[i] ?? null, docIds[i] ?? null])\n\n        const query = `\n        INSERT INTO \"${tableName}\" (key, namespace, updated_at, group_id, doc_id)\n        VALUES (?, ?, ?, ?, ?)","sourceCodeStart":233,"sourceCodeEnd":269,"githubUrl":"https://github.com/FlowiseAI/Flowise/blob/abe4a8601a058047b350c260676826e21dd14101/packages/components/nodes/recordmanager/SQLiteRecordManager/SQLiteRecordManager.ts#L233-L269","documentation":"SQLite analog of errors 263/268. Thrown in SQLiteRecordManager.update when timeAtLeast is set and the DB clock is older. Note: SQLite time comes from the same process (the file is local), so this almost always indicates the local system clock is behind, or the caller passed an unreasonable timeAtLeast.","triggerScenarios":"Local machine clock set ahead of when records were last written; caller passed a future timestamp; VM/container clock drift; testing with mocked clocks.","commonSituations":"Developer laptop clock skewed; CI runner clock drift; unit tests with hardcoded future timestamps.","solutions":["Check the system clock and NTP sync.","Drop timeAtLeast from updateOptions if not needed.","In tests, freeze both this.getTime()'s source and timeAtLeast consistently."],"exampleFix":"// before\nawait mgr.update(keys, { timeAtLeast: futureTimestamp })\n// after\nawait mgr.update(keys)","handlingStrategy":"validation","validationCode":"// For SQLite the clock is local; ensure system time is sane.\nconst now = Date.now()\nif (timeAtLeast && timeAtLeast > now) {\n  throw new Error('timeAtLeast is in the future relative to local clock.')\n}","typeGuard":"function isReasonableTimestamp(t: unknown, reference: number): t is number {\n  return typeof t === 'number' && t <= reference + 60_000\n}","tryCatchPattern":"try {\n  await manager.update(keys, { timeAtLeast })\n} catch (e) {\n  if (e instanceof Error && e.message.startsWith('Time sync issue')) {\n    // for SQLite this is local clock skew; sync NTP or drop timeAtLeast\n  }\n  throw e\n}","preventionTips":["Sync the OS clock via NTP.","Drop timeAtLeast unless dedup strictly needs it.","In tests, keep timeAtLeast consistent with the mocked time source."],"tags":["sqlite","record-manager","time-sync","clock-drift"],"backgroundTag":null,"analyzedSha":"abe4a8601a058047b350c260676826e21dd14101","analyzedAt":"2026-08-12T16:04:40.823Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}