hibernate/hibernate-orm · error · IllegalArgumentException

Unable to interpret LockMode reference from incoming externa

Error message

Unable to interpret LockMode reference from incoming external form: " + externalForm

What it means

LockMode.fromExternalForm(String) matches the incoming string case-insensitively against each LockMode's external form (the lowercased enum name, with underscores converted to hyphens for upgrade-nowait/upgrade-skiplocked) plus the legacy alias 'upgrade'. Any string that matches none of them - a typo, an old name, or free-form user input - throws IllegalArgumentException.

Source

Thrown at hibernate-core/src/main/java/org/hibernate/LockMode.java:324

		};
	}

	public static LockMode fromExternalForm(String externalForm) {
		if ( externalForm == null ) {
			return NONE;
		}

		for ( LockMode lockMode : values() ) {
			if ( lockMode.toExternalForm().equalsIgnoreCase( externalForm ) ) {
				return lockMode;
			}
		}

		if ( externalForm.equalsIgnoreCase( "upgrade" ) ) {
			return PESSIMISTIC_WRITE;
		}

		throw new IllegalArgumentException( "Unable to interpret LockMode reference from incoming external form: " + externalForm );
	}

	/**
	 * @return an instance of {@link LockOptions} with this lock mode, and
	 *         all other settings defaulted.
	 *
	 * @deprecated With no replacement; {@linkplain LockOptions} is no longer considered an API.
	 */
	@Deprecated(since = "7", forRemoval = true)
	public LockOptions toLockOptions() {
		return switch (this) {
			case NONE -> new LockOptions();
			case READ -> new LockOptions( READ );
			case OPTIMISTIC -> new LockOptions( OPTIMISTIC );
			case OPTIMISTIC_FORCE_INCREMENT -> new LockOptions( OPTIMISTIC_FORCE_INCREMENT );
			case UPGRADE_NOWAIT -> new LockOptions( PESSIMISTIC_WRITE, NO_WAIT_MILLI, PessimisticLockScope.NORMAL, Locking.FollowOn.ALLOW );
			case UPGRADE_SKIPLOCKED -> new LockOptions( PESSIMISTIC_WRITE, SKIP_LOCKED_MILLI, PessimisticLockScope.NORMAL, Locking.FollowOn.ALLOW );
			case PESSIMISTIC_READ -> new LockOptions( PESSIMISTIC_READ );

View on GitHub (pinned to fad1729dce)

Solutions

  1. Use the exact external forms: none, read, write, optimistic, optimistic_force_increment, pessimistic_read, pessimistic_write, pessimistic_force_increment, upgrade-nowait, upgrade-skiplocked (legacy alias: upgrade).
  2. Prefer passing LockMode constants directly instead of strings when the value is in your control.
  3. Trim and normalize the string, and validate it against LockMode.values()/toExternalForm() before calling.
  4. Catch IllegalArgumentException and fall back to a safe default (e.g. LockMode.NONE) for external input.

Example fix

// before - underscore form never matches
LockMode mode = LockMode.fromExternalForm("UPGRADE_NOWAIT");

// after - valid external form (hyphenated, case-insensitive)
LockMode mode = LockMode.fromExternalForm("upgrade-nowait");
Defensive patterns

Strategy: validation

Validate before calling

static Optional<LockMode> tryParseLockMode(String raw) {
    if (raw == null) return Optional.of(LockMode.NONE);
    String s = raw.trim();
    for (LockMode m : LockMode.values()) {
        if (m.toExternalForm().equalsIgnoreCase(s)) return Optional.of(m);
    }
    if (s.equalsIgnoreCase("upgrade")) return Optional.of(LockMode.PESSIMISTIC_WRITE);
    return Optional.empty();
}

Optional<LockMode> mode = tryParseLockMode(input);
LockMode lockMode = mode.orElseThrow(() -> new IllegalArgumentException("Unknown lock mode: " + input));

Type guard

static boolean isValidLockModeExternalForm(String raw) {
    return tryParseLockMode(raw).isPresent();
}

Try / catch

try {
    LockMode mode = LockMode.fromExternalForm(input);
} catch (IllegalArgumentException e) {
    log.warn("Ignoring unknown lock mode '{}', defaulting to NONE", input);
    mode = LockMode.NONE; // never let free-form input crash request handling
}

Prevention

When it happens

Trigger: Calling LockMode.fromExternalForm("pessimistic write"), fromExternalForm("FORCE"), or fromExternalForm("UPGRADE_NOWAIT") (correct form is 'upgrade-nowait'). Typically the string comes from XML config, REST query parameters, or property files that name a lock mode.

Common situations: Parsing lock-mode strings from external configuration or user input; code migrating from Hibernate 5-era names (e.g. old 'FORCE' / underscore forms); whitespace or case variants slipping in ('READ ' with a trailing space).

Related errors


AI-assisted analysis of hibernate/hibernate-orm@fad1729dce (2026-08-22). Data as JSON: /api/errors/a4539a75b4e9b561. Report an issue: GitHub.