apache/dolphinscheduler · error · UnsupportedOperationException

The data type: of the key: cannot be updated

Error message

The data type:  of the key:  cannot be updated

What it means

JdbcRegistryDataManager.putJdbcRegistryData rejects updating an existing key with a different data type (PERSISTENT vs EPHEMERAL). Once a key is created with a type, its type is immutable; writes that would change it throw UnsupportedOperationException. This protects clients that rely on ephemeral semantics (session-bound lifetime) from being silently converted to persistent data.

Source

Thrown at dolphinscheduler-registry/dolphinscheduler-registry-plugins/dolphinscheduler-registry-jdbc/src/main/java/org/apache/dolphinscheduler/plugin/registry/jdbc/server/JdbcRegistryDataManager.java:181

        return jdbcRegistryDataRepository.selectAll()
                .stream()
                .filter(jdbcRegistryDataDTO -> KeyUtils.isParent(key, jdbcRegistryDataDTO.getDataKey()))
                .collect(Collectors.toList());
    }

    @Override
    public void putJdbcRegistryData(Long clientId, String key, String value, DataType dataType) {
        checkNotNull(clientId);
        checkNotNull(key);
        checkNotNull(dataType);

        final Optional<JdbcRegistryDataDTO> jdbcRegistryDataOptional = jdbcRegistryDataRepository.selectByKey(key);

        jdbcRegistryTransactionTemplate.execute(status -> {
            if (jdbcRegistryDataOptional.isPresent()) {
                JdbcRegistryDataDTO jdbcRegistryData = jdbcRegistryDataOptional.get();
                if (!dataType.name().equals(jdbcRegistryData.getDataType())) {
                    throw new UnsupportedOperationException("The data type: " + jdbcRegistryData.getDataType()
                            + " of the key: " + key + " cannot be updated");
                }

                if (DataType.EPHEMERAL.name().equals(jdbcRegistryData.getDataType())) {
                    if (!jdbcRegistryData.getClientId().equals(clientId)) {
                        throw new UnsupportedOperationException(
                                "The EPHEMERAL data: " + key + " can only be updated by its owner: "
                                        + jdbcRegistryData.getClientId() + " but not: " + clientId);
                    }
                }

                jdbcRegistryData.setDataValue(value);
                jdbcRegistryData.setLastUpdateTime(new Date());
                jdbcRegistryDataRepository.updateById(jdbcRegistryData);

                JdbcRegistryDataChangeEventDTO jdbcRegistryDataChangeEvent = JdbcRegistryDataChangeEventDTO.builder()
                        .jdbcRegistryData(jdbcRegistryData)
                        .eventType(JdbcRegistryDataChangeEventDTO.EventType.UPDATE)

View on GitHub (pinned to 02eac45a1b)

Solutions

  1. Delete the existing key first, then put it again with the desired DataType
  2. Keep a single shared constant/helper for the DataType of each well-known registry key
  3. Check the existing data type via get/getChildren before writing and only set dataValue

Example fix

// before
registryClient.put(key, value, DataType.PERSISTENT); // key was EPHEMERAL
// after
if (registryClient.get(key) != null) {
    registryClient.remove(key);
}
registryClient.put(key, value, DataType.PERSISTENT);
Defensive patterns

Strategy: validation

Validate before calling

Optional<JdbcRegistryDataDTO> existing = repository.selectByKey(key); DataType effective = existing.map(d -> DataType.valueOf(d.getDataType())).orElse(desired);

Try / catch

try { manager.putJdbcRegistryData(key, value, dataType, clientId); } catch (UnsupportedOperationException e) { log.error("Type conflict on {}: {}", key, e.getMessage()); }

Prevention

When it happens

Trigger: Calling putData on a key that already exists as EPHEMERAL with DataType.PERSISTENT, or vice versa; re-creating a key after a client restart but passing a different DataType than originally used.

Common situations: Changing an ephemeral session node to persistent (or back) during refactoring, two components sharing one registry key but constructing DataType differently, stale local code that assumes the key was deleted.

Understand the failure class

Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.

Related errors


AI-assisted analysis of apache/dolphinscheduler@02eac45a1b (2026-09-06). Data as JSON: /api/errors/92b276219fd03994. Report an issue: GitHub.