pinpoint-apm/pinpoint · critical · IllegalStateException
Corrupted schema change logs. Duplicate change set :
Error message
Corrupted schema change logs. Duplicate change set :
What it means
Thrown by SchemaChangeLogServiceImpl.getSchemaChangeLogs when the same change set id appears in more than one change log record. The schemaChangeLogIds set add() returning false detects the duplicate id. Each change set may be executed and logged only once, so duplicate ids mean the log history is corrupted.
Source
Thrown at hbase/hbase-schema/src/main/java/com/navercorp/pinpoint/hbase/schema/service/SchemaChangeLogServiceImpl.java:124
.build();
schemaChangeLogDao.insertChangeLog(namespace, schemaChangeLog);
return schemaChangeLog;
}
@Override
public List<SchemaChangeLog> getSchemaChangeLogs(String namespace) {
List<SchemaChangeLog> schemaChangeLogs = schemaChangeLogDao.getChangeLogs(namespace);
Map<Integer, SchemaChangeLog> orderedSchemaChangeLogs = new TreeMap<>();
Set<String> schemaChangeLogIds = new HashSet<>();
for (SchemaChangeLog schemaChangeLog : schemaChangeLogs) {
Integer execOrder = schemaChangeLog.getExecOrder();
String id = schemaChangeLog.getId();
SchemaChangeLog previousLog = orderedSchemaChangeLogs.put(execOrder, schemaChangeLog);
if (previousLog != null) {
throw new IllegalStateException("Corrupted schema change logs. Duplicate order for change set : " + id);
}
if (!schemaChangeLogIds.add(schemaChangeLog.getId())) {
throw new IllegalStateException("Corrupted schema change logs. Duplicate change set : " + id);
}
}
return new ArrayList<>(orderedSchemaChangeLogs.values());
}
@Override
public SchemaChangeLog getSchemaChangeLog(String namespace, String id) {
Assert.hasLength(id, "namespace must not be empty");
return schemaChangeLogDao.getChangeLog(namespace, id);
}
}
View on GitHub (pinned to 744c3d3075)
Solutions
- Delete or merge the duplicate log rows so each change set id appears exactly once.
- Restore the change log table from a backup created before the duplicate rows appeared.
- Add/keep a uniqueness guarantee (id as row key) so writes of the same change set are idempotent, and serialize schema updates with a lock.
- If the history is unrecoverable, re-initialize the namespace schema from scratch.
Example fix
// before: two rows with id='create-agent-table' // after: keep the first executed record, remove the extra row delete 'schema_change_log', 'create-agent-table-dup'
Defensive patterns
Strategy: try-catch
Validate before calling
Map<String, Long> byId = logs.stream()
.collect(Collectors.groupingBy(SchemaChangeLog::getId, Collectors.counting()));
boolean corrupt = byId.values().stream().anyMatch(c -> c > 1); Try / catch
try { logs = schemaChangeLogService.getSchemaChangeLogs(namespace); } catch (IllegalStateException e) {
if (e.getMessage().contains("Duplicate change set")) { alertOps("Corrupt change log: duplicate changeSet id"); }
} Prevention
- Use changeSet id as the row key so duplicate writes overwrite instead of appending
- Block manual edits of the log table
- Guard retry logic so a completed change set is never logged twice
When it happens
Trigger: Reading change logs where two rows share an identical id (changeSetId) value, regardless of their execOrder values.
Common situations: Retried schema updates that double-wrote the log row (missing uniqueness on id); manual inserts into the log table; restoring a backup over a live table creating overlapping rows; concurrent writers racing to log the same change set.
Understand the failure class
Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.
Related errors
- Corrupted schema change logs. Duplicate order for change set
- Duplicate ColumnFamily name :
- Connection already closed
- Invalid namespace : <namespace>
- Already closed
AI-assisted analysis of pinpoint-apm/pinpoint@744c3d3075 (2026-09-07).
Data as JSON: /api/errors/d6ba136945344e9b.
Report an issue: GitHub.