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

  1. Rename/move the stale .h2.db (PageStore) file out of the directory if the live database is MVStore
  2. Explicitly set the driver profile (h2-v1 for PageStore or h2-v3 for MVStore) to bypass auto-detection
  3. 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

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


AI-assisted analysis of t8y2/dbx@c0390bff16 (2026-09-05). Data as JSON: /api/errors/7582ac5d40899735. Report an issue: GitHub.