t8y2/dbx · error · IOException
Both H2 PageStore and MVStore files exist for " + base + ";
Error message
Both H2 PageStore and MVStore files exist for " + base + "; choose an explicit H2 driver profile
What it means
H2FileFormatDetector distinguishes PageStore (base.h2.db) from MVStore (base.mv.db) databases by checking which files exist. When BOTH exist it cannot decide which engine owns the data and refuses to guess, raising an IOException that asks for an explicit driver profile.
Source
Thrown at agents/drivers/h2/src/main/java/com/dbx/agent/h2/H2FileFormatDetector.java:29
import java.util.OptionalInt;
final class H2FileFormatDetector {
private static final int BLOCK_SIZE = 4096;
private H2FileFormatDetector() {
}
static OptionalInt detect(String jdbcUrl) throws IOException {
Path base = localDatabaseBasePath(jdbcUrl);
if (base == null) {
return OptionalInt.empty();
}
Path pageStore = withSuffix(base, ".h2.db");
Path mvStore = withSuffix(base, ".mv.db");
boolean pageStoreExists = Files.isRegularFile(pageStore);
boolean mvStoreExists = Files.isRegularFile(mvStore);
if (pageStoreExists && mvStoreExists) {
throw new IOException("Both H2 PageStore and MVStore files exist for " + base + "; choose an explicit H2 driver profile");
}
if (pageStoreExists) {
return OptionalInt.of(1);
}
if (!mvStoreExists) {
return OptionalInt.empty();
}
return OptionalInt.of(readMvStoreFormat(mvStore));
}
private static int readMvStoreFormat(Path file) throws IOException {
byte[] bytes;
try (InputStream input = Files.newInputStream(file)) {
bytes = input.readNBytes(BLOCK_SIZE * 2);
}
if (bytes.length < BLOCK_SIZE * 2) {
throw new IOException("H2 MVStore file is too small to contain valid headers: " + file);
}View on GitHub (pinned to c0390bff16)
Solutions
- Rename/move the stale .h2.db (PageStore) file out of the directory if the live database is MVStore
- Explicitly set the driver profile (h2-v1 for PageStore or h2-v3 for MVStore) to bypass auto-detection
- Determine which file is current (check mtimes/open transaction logs) before deleting anything
Example fix
// before
detect(Path.of("/data/app")); // both /data/app.h2.db and /data/app.mv.db exist
// after
mv /data/app.h2.db /data/app.h2.db.old # then auto-detect succeeds
// or: profile = "h2-v3" Defensive patterns
Strategy: validation
Validate before calling
boolean page = Files.isRegularFile(Path.of(base + ".h2.db"));
boolean mv = Files.isRegularFile(Path.of(base + ".mv.db"));
if (page && mv) throw new IllegalStateException("both PageStore and MVStore files present for " + base); Try / catch
try {
return H2Agent.connect(params);
} catch (IOException e) {
if (e.getMessage().contains("Both H2 PageStore and MVStore")) {
params.put("driverProfile", "h2-v3"); // or clean up stale file
}
throw e;
} Prevention
- After migrating from H2 1.4- (PageStore) to MVStore, archive or delete old .h2.db files
- Keep backups out of the live database directory
- Set an explicit driver profile for databases where both artifacts may appear
When it happens
Trigger: detect(base) finds both '<base>.h2.db' and '<base>.mv.db' as regular files in the database directory.
Common situations: Database was migrated from old H2 (1.4-) to MVStore and leftover .h2.db files were never removed; backup files placed next to live DB; a copy of another database dropped into the same directory.
Related errors
- Custom H2 JDBC JAR does not exist: " + path
- Unsupported H2 database file format: " + format
- H2 MVStore file is too small to contain valid headers: " + f
- H2 JDBC driver was not loaded
- H2 JDBC driver rejected URL: " + buildJdbcUrl(params)
AI-assisted analysis of t8y2/dbx@c0390bff16 (2026-09-05).
Data as JSON: /api/errors/7582ac5d40899735.
Report an issue: GitHub.