apereo/cas · warning

Unable to locate [ ] in the message header

Error message

Unable to locate [{}] in the message header

What it means

The QR WebSocket channel controller's verify() (@MessageMapping /accept) requires STOMP native headers to carry the QR channel id. When the incoming STOMP message lacks that header, it logs this warning and returns false instead of proceeding, preventing the QR login from being accepted.

Solutions

  1. Set the QR channel id as a STOMP native header on the /accept message, matching QRAuthenticationConstants.QR_AUTHENTICATION_CHANNEL_ID exactly.
  2. Verify the client library sends custom headers as STOMP native headers (they appear under 'nativeHeaders' server-side), e.g. stompClient.send('/qr/accept', {'qrChannelId': id}, body).
  3. Check header name spelling/case against the constant value in QRAuthenticationConstants.
  4. Confirm no intermediary (gateway/sockjs config) is dropping custom STOMP headers.

Example fix

// before
stompClient.send('/qr/accept', {}, payload);
// after
stompClient.send('/qr/accept', { 'qrChannelId': channelId, 'qrDeviceId': deviceId }, payload);
Defensive patterns

Strategy: validation

Validate before calling

const headers = stompMessage.headers || {};
if (!headers['qrChannelId']) {
  return Promise.reject(new Error('qrChannelId native header is required'));
}

Prevention

When it happens

Trigger: A STOMP client sends a message to /qr/accept without a native header named by QRAuthenticationConstants.QR_AUTHENTICATION_CHANNEL_ID; the nativeHeaders map exists but does not contain the key.

Common situations: Custom WebSocket/STOMP clients that forget to set STOMP custom headers; header names mismatched in case or spelling; middleware/proxy stripping STOMP user headers; client library versions that send custom headers differently.

Understand the failure class

Background: "missing required argument" and "the following required arguments were not provided": what required-argument errors mean and how to fix them — this error's family across 20 libraries.

Related errors


AI-assisted analysis of apereo/cas@e7288fc434 (2026-09-08). Data as JSON: /api/errors/074704859f9047f6. Report an issue: GitHub.

Appendix: source

Thrown at support/cas-server-support-qr-authentication/src/main/java/org/apereo/cas/qr/web/QRAuthenticationChannelController.java:52

        .defaultTypingEnabled(false).build().toObjectMapper();

    private final MessageSendingOperations<String> messageTemplate;

    private final QRAuthenticationTokenValidatorService tokenValidatorService;

    /**
     * Verify.
     *
     * @param message the message
     * @return true/false
     */
    @MessageMapping("/accept")
    public boolean verify(final Message<String> message) {
        val payload = message.getPayload();
        LOGGER.trace("Received payload [{}]", payload);
        val nativeHeaders = Objects.requireNonNull(message.getHeaders().get("nativeHeaders", LinkedMultiValueMap.class));
        if (!nativeHeaders.containsKey(QRAuthenticationConstants.QR_AUTHENTICATION_CHANNEL_ID)) {
            LOGGER.warn("Unable to locate [{}] in the message header", QRAuthenticationConstants.QR_AUTHENTICATION_CHANNEL_ID);
            return false;
        }
        if (!nativeHeaders.containsKey(QRAuthenticationConstants.QR_AUTHENTICATION_DEVICE_ID)) {
            LOGGER.warn("Unable to locate [{}] in the message header", QRAuthenticationConstants.QR_AUTHENTICATION_DEVICE_ID);
            return false;
        }

        val channelId = Objects.requireNonNull(nativeHeaders.get(QRAuthenticationConstants.QR_AUTHENTICATION_CHANNEL_ID)).getFirst();
        val endpoint = String.format("%s/%s/verify", QRAuthenticationConstants.QR_SIMPLE_BROKER_DESTINATION_PREFIX, channelId);
        try {
            LOGGER.debug("Current channel id is [{}]", channelId);
            val resultMap = MAPPER.readValue(payload, new TypeReference<Map<String, String>>() {
            });
            val token = resultMap.get(TokenConstants.PARAMETER_NAME_TOKEN);

            val deviceId = Objects.requireNonNull(nativeHeaders.get(QRAuthenticationConstants.QR_AUTHENTICATION_DEVICE_ID)).getFirst().toString();
            val validationRequest = QRAuthenticationTokenValidationRequest.builder()
                .deviceId(deviceId)

View on GitHub (pinned to e7288fc434)