{"record":{"id":"e30ee616f95cb936","repo":"apereo/cas","slug":"unable-to-get-entity-from-mdq-server-and-a-backup","errorCode":null,"errorMessage":"Unable to get entity from MDQ server and a backup file does not exist.","messagePattern":"Unable to get entity from MDQ server and a backup file does not exist\\.","errorType":"exception","errorClass":"SamlException","httpStatus":null,"severity":"error","filePath":"support/cas-server-support-saml-idp-metadata/src/main/java/org/apereo/cas/support/saml/services/idp/metadata/cache/resolver/MetadataQueryProtocolMetadataResolver.java","lineNumber":65,"sourceCode":"    public boolean supports(final SamlRegisteredService service) {\n        val locations = org.springframework.util.StringUtils.commaDelimitedListToSet(\n            SpringExpressionLanguageValueResolver.getInstance().resolve(service.getMetadataLocation())\n        );\n        return locations.stream().anyMatch(SamlUtils::isDynamicMetadataQueryConfigured);\n    }\n\n    @Override\n    protected boolean shouldHttpResponseStatusBeProcessed(final HttpStatus status) {\n        return true;\n    }\n\n    @Override\n    protected AbstractMetadataResolver getMetadataResolverFromResponse(final HttpResponse response, final File backupFile) throws Exception {\n        if (!HttpStatus.valueOf(response.getCode()).is2xxSuccessful()) {\n            if (Files.exists(backupFile.toPath())) {\n                return new InMemoryResourceMetadataResolver(backupFile, this.configBean);\n            }\n            throw new SamlException(\"Unable to get entity from MDQ server and a backup file does not exist.\");\n        }\n        val entity = ((HttpEntityContainer) response).getEntity();\n        val result = IOUtils.toString(entity.getContent(), StandardCharsets.UTF_8);\n        val path = backupFile.toPath();\n        LOGGER.trace(\"Writing metadata to file at [{}]\", path);\n        try (val output = Files.newBufferedWriter(path, StandardCharsets.UTF_8)) {\n            IOUtils.write(result, output);\n            output.flush();\n            StreamSupport.stream(path.getFileSystem().getFileStores().spliterator(), false)\n                .filter(store -> store.supportsFileAttributeView(UserDefinedFileAttributeView.class))\n                .forEach(store -> setFileAttribute(response, backupFile));\n        }\n        EntityUtils.consume(entity);\n        return new InMemoryResourceMetadataResolver(backupFile, configBean);\n    }\n\n    @Override\n    protected HttpResponse fetchMetadata(final SamlRegisteredService service,","sourceCodeStart":47,"sourceCodeEnd":83,"githubUrl":"https://github.com/apereo/cas/blob/e7288fc434b4f4505b8452e1a57e8fb3111bb863/support/cas-server-support-saml-idp-metadata/src/main/java/org/apereo/cas/support/saml/services/idp/metadata/cache/resolver/MetadataQueryProtocolMetadataResolver.java#L47-L83","documentation":"Thrown by the MDQ (Metadata Query Protocol) resolver when the HTTP response from the MDQ server is not a 2xx success AND no backup metadata file exists on disk for the entity. The resolver normally caches fetched metadata to a backup file so it can serve it during outages; without either a live response or a cached copy, metadata resolution cannot proceed.","triggerScenarios":"Calling resolveFromMetadataQueryProtocolServer (via getMetadataResolverFromResponse) when the MDQ server returns a non-2xx status (404 for unknown entityID, 500, timeout at gateway) and backupFile does not exist on disk (first fetch never succeeded or the file was deleted).","commonSituations":"MDQ server down or misconfigured URL in cas.authn.saml.idp.metadata.query-protocol.*; wrong entityID requested (404); firewall/proxy blocking outbound HTTP; first-time setup with no cached backup; backup directory wiped or not writable in a container restart.","solutions":["Verify the MDQ server URL and that it responds 200 for the entityID (curl the query URL).","Check network/proxy connectivity from the CAS server to the MDQ server.","If metadata is available from a prior fetch, restore the backup file to the configured location.","Fix the entityID configured on the SAML registered service so MDQ can find the entity.","Configure a reachable MDQ endpoint or switch the service to static metadata."],"exampleFix":"// before\ncas.authn.saml.idp.metadata.query-protocol.url=https://wrong-mdq.example.org/idp\n// after\ncas.authn.saml.idp.metadata.query-protocol.url=https://mdq.example.org/mdq","handlingStrategy":"fallback","validationCode":"val queryUrl = mdqBaseUrl + \"/entities/\" + EncodingUtils.urlEncode(entityId);\nif (!Files.exists(backupPath)) {\n    try (var conn = new URL(queryUrl).openConnection()) { conn.connect(); }\n}","typeGuard":"boolean hasBackup(File f) { return f != null && Files.exists(f.toPath()); }","tryCatchPattern":"try {\n    return resolver.resolve(criteriaSet);\n} catch (SamlException e) {\n    LOGGER.warn(\"MDQ fetch failed, trying backup metadata\", e);\n    return loadFromStaticBackup(criteriaSet);\n}","preventionTips":["Pre-seed the MDQ backup directory with known IdP metadata before first deployment.","Monitor MDQ server availability and alert on non-2xx responses.","Validate the MDQ URL configuration in staging before production.","Mount backup metadata on persistent storage in containers."],"tags":["saml","mdq","metadata","network","http"],"backgroundTag":"http-non-200-response","analyzedSha":"e7288fc434b4f4505b8452e1a57e8fb3111bb863","analyzedAt":"2026-09-08T15:39:16.015Z","contentChangedAt":"2026-09-08T15:39:16.015Z","schemaVersion":2},"datasetVersion":"2026-09-15T23:17:13.987Z"}