spring-projects/spring-security · error · IOException

Failed to deserialize asserting party credential collection

Error message

Failed to deserialize asserting party credential collection

What it means

JdbcAssertingPartyMetadataRepository stores the asserting party's credential collection as a Java-serialized blob. deserialize() wraps ClassNotFoundException (a credential class missing on the classpath) into an IOException with this message, i.e. the stored blob references classes that cannot be loaded.

Solutions

  1. Align the application's Spring Security version with the one that wrote the rows, or re-write rows in the current format
  2. Prefer inserting credentials via the repository's modern column-based APIs instead of raw serialized blobs
  3. Inspect the cause ClassNotFoundException to find the missing class and add the dependency providing it
  4. Re-export metadata/credentials from the source environment into the target database
  5. Add the AllowlistObjectInputFilter-consistent classes to the classpath if a custom credential type was serialized

Example fix

// before: reading old serialized blobs across versions
var creds = repository.findByEntityId(id); // IOException: ClassNotFoundException
// after: re-write rows with current version, or run migration
migrationRewriteCredentialBlobs(dataSource);
var creds = repository.findByEntityId(id);
Defensive patterns

Strategy: try-catch

Try / catch

try {
    var metadata = repository.findByEntityId(entityId);
} catch (DataAccessException | Saml2Exception ex) {
    if (ex.getCause() instanceof IOException io && io.getCause() instanceof ClassNotFoundException cnf) {
        logger.error("Serialized blob references missing class {} — re-write rows", cnf.getMessage());
    }
    throw ex;
}

Prevention

When it happens

Trigger: Reading rows from the database whose credentials blob was written by a different Spring Security version or environment, and ObjectInputStream cannot resolve a class in the serialized credential graph.

Common situations: Spring Security upgraded/downgraded between write and read so serialized class signatures changed; reading a database written by a different application/JVM lacking saml classes; corrupted rows; restoring dumps across environments.

Related errors


AI-assisted analysis of spring-projects/spring-security@96852e8860 (2026-09-10). Data as JSON: /api/errors/56046e0893aec7c7. Report an issue: GitHub.

Appendix: source

Thrown at saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/registration/JdbcAssertingPartyMetadataRepository.java:282

				}
				ALLOWLIST = new AllowlistObjectInputFilter(classes);
			}

			@Override
			@SuppressWarnings("unchecked")
			public Collection<Saml2X509Credential> deserialize(InputStream in) throws IOException {
				ObjectInputStream oin = new ObjectInputStream(in);
				oin.setObjectInputFilter(ALLOWLIST);
				try {
					Collection<Saml2X509Credential> credentials = (Collection<Saml2X509Credential>) oin.readObject();
					for (Object credential : credentials) {
						Assert.isInstanceOf(Saml2X509Credential.class, credential,
								"Deserialized object is not of type Saml2X509Credential");
					}
					return credentials;
				}
				catch (ClassNotFoundException ex) {
					throw new IOException("Failed to deserialize asserting party credential collection", ex);
				}
			}

			private static final class AllowlistObjectInputFilter implements ObjectInputFilter {

				private static final Log logger = LogFactory.getLog(JdbcAssertingPartyMetadataRepository.class);

				private static final int MAX_DEPTH = 20;

				private static final int MAX_REFS = 1000;

				private static final int MAX_ARRAY = 16384;

				private static final int MAX_BYTES = 1_048_576;

				private final ObjectInputFilter delegate;

				private AllowlistObjectInputFilter(Set<String> allowlist) {

View on GitHub (pinned to 96852e8860)