apache/pulsar · error · ConflictException
Failed to update from old:%s to value:%s
Error message
Failed to update from old:%s to value:%s
What it means
ConflictException thrown by MetadataStoreTableViewImpl.put when the configured conflictResolver rejects the update: the resolver test(oldValue, newValue) returned false, so the read-modify-update on the cached entry is aborted with this formatted message showing old and new values.
Source
Thrown at pulsar-metadata/src/main/java/org/apache/pulsar/metadata/tableview/impl/MetadataStoreTableViewImpl.java:412
private String getKey(String path) {
return path.replaceFirst(pathPrefix + "/", "");
}
public boolean exists(String key) {
return immutableData.containsKey(key);
}
public T get(String key) {
return data.get(key);
}
public CompletableFuture<Void> put(String key, T value) {
String path = getPath(key);
return cache.readModifyUpdateOrCreate(path, (old) -> {
if (conflictResolver.test(old.orElse(null), value)) {
return value;
} else {
throw new ConflictException(
String.format("Failed to update from old:%s to value:%s", old, value));
}
}).thenCompose(__ -> doHandleNotification(path)) // immediately notify local tableview
.exceptionally(e -> {
if (e.getCause() instanceof MetadataStoreException.BadVersionException) {
throw FutureUtil.wrapToCompletionException(new ConflictException(
String.format("Failed to update to value:%s", value)));
}
throw FutureUtil.wrapToCompletionException(e.getCause());
});
}
public CompletableFuture<Void> delete(String key) {
String path = getPath(key);
return cache.delete(path)
.thenCompose(__ -> doHandleNotification(path)); // immediately notify local tableview
}View on GitHub (pinned to 820761864e)
Solutions
- Make the value pass the conflictResolver you configured, or adjust the resolver logic to accept the transition.
- Check the old value in the message to see why the transition was rejected (e.g. version/ordering regression).
- If the write is legitimate after a restart or state reset, delete the key first or seed the resolver with correct baseline state.
- Handle ConflictException from the returned CompletableFuture and re-read the current tableview state before retrying.
Example fix
// before .resolveConflict((old, next) -> next >= old) // rejects stale lower values // after .resolveConflict((old, next) -> next >= old || isReset(next))
Defensive patterns
Strategy: try-catch
Try / catch
tableView.put(key, value).exceptionally(ex -> {
Throwable c = FutureUtil.unwrapCompletionException(ex);
if (c instanceof ConflictException) {
// re-read state via tableview.get(key), reconcile, retry
}
throw ex instanceof RuntimeException ? (RuntimeException) ex : new RuntimeException(ex);
}); Prevention
- Design the conflictResolver to accept legitimate state transitions including restarts.
- Log old/new values from the ConflictException message for diagnosis.
- Avoid concurrent writers to the same tableview key; assign per-key ownership.
- Reconcile local state with the store before retrying rejected writes.
When it happens
Trigger: Calling tableview.put(key, value) where the existing value (or null when absent) fails the ConflictResolver predicate supplied at tableview construction, e.g. a resolver that requires monotonically increasing values receives an older value.
Common situations: Out-of-order writes from a producer applying stale state, concurrent writers fighting over the same key with a last-writer-loses-resolver rejecting, or putting a value that violates domain rules encoded in the resolver.
Related errors
- Concurrent modification
- Failed to set data for ${path}
- Failed to set/create ${path}
- Migration is already in progress (phase: ${phase})
- ${name} failed to fill existing items in ${secs} secs. Fille
AI-assisted analysis of apache/pulsar@820761864e (2026-09-06).
Data as JSON: /api/errors/fa1abe19164993c7.
Report an issue: GitHub.