fix: graceful recovery for IDB schema version conflict
CI / Build & Quality Checks (push) Has been cancelled

When matrix-sdk is briefly upgraded then reverted, the local IndexedDB
schema version is higher than the SDK expects. Detect the VersionError
DOMException and show a clear 'Clear local data and reload' button
instead of a cryptic error message.
This commit is contained in:
Lotus Bot
2026-05-21 23:50:24 -04:00
parent 6b54926552
commit 41bf176919
2 changed files with 38 additions and 7 deletions
+19
View File
@@ -19,6 +19,7 @@ import React, { MouseEventHandler, ReactNode, useCallback, useEffect, useState }
import { import {
clearCacheAndReload, clearCacheAndReload,
clearLoginData, clearLoginData,
IDB_VERSION_CONFLICT,
initClient, initClient,
logoutClient, logoutClient,
startClient, startClient,
@@ -201,16 +202,34 @@ export function ClientRoot({ children }: ClientRootProps) {
<Dialog> <Dialog>
<Box direction="Column" gap="400" style={{ padding: config.space.S400 }}> <Box direction="Column" gap="400" style={{ padding: config.space.S400 }}>
{loadState.status === AsyncStatus.Error && ( {loadState.status === AsyncStatus.Error && (
<>
{loadState.error.message === IDB_VERSION_CONFLICT ? (
<>
<Text>
Local data is from a newer app version and cannot be read. Clear local
data to continue (you will need to log in again).
</Text>
<Button variant="Critical" onClick={clearLoginData}>
<Text as="span" size="B400">
Clear local data and reload
</Text>
</Button>
</>
) : (
<Text>{`Failed to load. ${loadState.error.message}`}</Text> <Text>{`Failed to load. ${loadState.error.message}`}</Text>
)} )}
</>
)}
{startState.status === AsyncStatus.Error && ( {startState.status === AsyncStatus.Error && (
<Text>{`Failed to start. ${startState.error.message}`}</Text> <Text>{`Failed to start. ${startState.error.message}`}</Text>
)} )}
{loadState.error?.message !== IDB_VERSION_CONFLICT && (
<Button variant="Critical" onClick={mx ? () => startMatrix(mx) : loadMatrix}> <Button variant="Critical" onClick={mx ? () => startMatrix(mx) : loadMatrix}>
<Text as="span" size="B400"> <Text as="span" size="B400">
Retry Retry
</Text> </Text>
</Button> </Button>
)}
</Box> </Box>
</Dialog> </Dialog>
</Box> </Box>
+12
View File
@@ -11,6 +11,10 @@ type Session = {
deviceId: string; deviceId: string;
}; };
// Thrown when the local IndexedDB has a higher schema version than this SDK expects.
// This happens after a downgrade (e.g. matrix-js-sdk was briefly upgraded and then reverted).
export const IDB_VERSION_CONFLICT = 'IDB_VERSION_CONFLICT';
export const initClient = async (session: Session): Promise<MatrixClient> => { export const initClient = async (session: Session): Promise<MatrixClient> => {
const indexedDBStore = new IndexedDBStore({ const indexedDBStore = new IndexedDBStore({
indexedDB: global.indexedDB, indexedDB: global.indexedDB,
@@ -32,7 +36,15 @@ export const initClient = async (session: Session): Promise<MatrixClient> => {
verificationMethods: ['m.sas.v1'], verificationMethods: ['m.sas.v1'],
}); });
try {
await indexedDBStore.startup(); await indexedDBStore.startup();
} catch (e) {
// IDB VersionError = local DB was written by a newer SDK version (schema downgrade).
if (e instanceof DOMException && e.name === 'VersionError') {
throw new Error(IDB_VERSION_CONFLICT);
}
throw e;
}
await mx.initRustCrypto(); await mx.initRustCrypto();
mx.setMaxListeners(50); mx.setMaxListeners(50);