{"record":{"id":"f44fdc62905d2376","repo":"FlowiseAI/Flowise","slug":"time-sync-issue-with-database-updatedat-tim","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/MySQLRecordManager/MySQLrecordManager.ts","lineNumber":306,"sourceCode":"        } 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\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":288,"sourceCodeEnd":324,"githubUrl":"https://github.com/FlowiseAI/Flowise/blob/abe4a8601a058047b350c260676826e21dd14101/packages/components/nodes/recordmanager/MySQLRecordManager/MySQLrecordManager.ts#L288-L324","documentation":"Thrown inside MySQLRecordManager.update when the caller passes updateOptions.timeAtLeast and the database's current time (this.getTime()) is older than that floor. The record manager uses updated_at for staleness checks, so a clock skewed backwards would silently write stale markers; this guard refuses the write.","triggerScenarios":"The MySQL server (or its host) clock is behind the Flowise server clock; an NTP sync failure on the DB host; container clock drift; running across VMs in different time sources; the caller passed a future-dated timeAtLeast by mistake.","commonSituations":"Docker container whose clock drifted; cloud DB failover resetting the time; developer's machine set to a future date; ingestion pipeline computing timeAtLeast from a monotonic clock that outpaced wall-clock.","solutions":["Sync clocks: ensure both the Flowise host and the MySQL host run NTP/chrony and agree to within a second.","If skew is expected and acceptable for your workload, drop or relax updateOptions.timeAtLeast.","Verify this.getTime() returns DB time (SELECT NOW()) and not application time; check the getTime implementation.","Restart drifted containers - Docker Desktop especially can drift after host sleep."],"exampleFix":"// before\nawait mgr.update(keys, { timeAtLeast: Date.now() })\n// after - let the manager derive the floor, or pass a value tied to DB time\nawait mgr.update(keys)","handlingStrategy":"retry","validationCode":"// Compare app clock to DB clock before calling update.\nconst dbNow = await manager.getTime()\nconst skew = Math.abs(Date.now() - dbNow)\nif (skew > 5_000) {\n  throw new Error(`Clock skew of ${skew}ms detected; sync NTP before ingesting.`)\n}","typeGuard":"function isReasonableTimestamp(t: unknown, reference: number): t is number {\n  return typeof t === 'number' && t <= reference + 60_000\n}","tryCatchPattern":"// Transient clock skew may resolve via NTP; retry once after a short delay only if skew is small.\ntry {\n  await manager.update(keys, { timeAtLeast })\n} catch (e) {\n  if (e instanceof Error && e.message.startsWith('Time sync issue')) {\n    // re-check skew, resync, and retry once; otherwise surface\n  }\n  throw e\n}","preventionTips":["Run chrony/ntpdate on every host running Flowise and every DB host.","In Docker, enable clock syncing on the host; restart containers after long host sleeps.","Avoid passing a hardcoded timeAtLeast; let the manager derive it."],"tags":["mysql","record-manager","time-sync","clock-drift","ntp"],"backgroundTag":null,"analyzedSha":"abe4a8601a058047b350c260676826e21dd14101","analyzedAt":"2026-08-12T16:04:40.823Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}