arduino/Arduino · error · Exception

Unexpected %s response header: %s

Error message

Unexpected %s response header: %s

What it means

The NTLM connection pool wraps urllib3's connection creation in an NTLM handshake: on connect it sends the negotiate header and then scans the WWW-Authenticate response header for a token starting with 'NTLM ' to extract the server challenge. If the response header contains no such NTLM token, _new_conn raises this generic Exception because the server did not reply with the NTLM challenge the handshake requires.

Source

Thrown at arduino-core/src/processing/app/i18n/python/requests/packages/urllib3/contrib/ntlmpool.py:83

        conn.request('GET', self.authurl, None, headers)
        res = conn.getresponse()
        reshdr = dict(res.getheaders())
        log.debug('Response status: %s %s' % (res.status, res.reason))
        log.debug('Response headers: %s' % reshdr)
        log.debug('Response data: %s [...]' % res.read(100))

        # Remove the reference to the socket, so that it can not be closed by
        # the response object (we want to keep the socket open)
        res.fp = None

        # Server should respond with a challenge message
        auth_header_values = reshdr[resp_header].split(', ')
        auth_header_value = None
        for s in auth_header_values:
            if s[:5] == 'NTLM ':
                auth_header_value = s[5:]
        if auth_header_value is None:
            raise Exception('Unexpected %s response header: %s' %
                            (resp_header, reshdr[resp_header]))

        # Send authentication message
        ServerChallenge, NegotiateFlags = \
            ntlm.parse_NTLM_CHALLENGE_MESSAGE(auth_header_value)
        auth_msg = ntlm.create_NTLM_AUTHENTICATE_MESSAGE(ServerChallenge,
                                                         self.user,
                                                         self.domain,
                                                         self.pw,
                                                         NegotiateFlags)
        headers[req_header] = 'NTLM %s' % auth_msg
        log.debug('Request headers: %s' % headers)
        conn.request('GET', self.authurl, None, headers)
        res = conn.getresponse()
        log.debug('Response status: %s %s' % (res.status, res.reason))
        log.debug('Response headers: %s' % dict(res.getheaders()))
        log.debug('Response data: %s [...]' % res.read()[:100])
        if res.status != 200:

View on GitHub (pinned to a0df6e0e83)

Solutions

  1. Inspect the raw WWW-Authenticate header returned by the server (curl -v or res.getheaders()) and confirm whether NTLM is offered.
  2. If the server only offers Negotiate/Kerberos, use a Kerberos-capable client (e.g. requests-kerberos) instead of NTLMConnectionPool, or enable NTLM on the server/proxy.
  3. Check for intermediaries (proxies, load balancers) stripping or rewriting WWW-Authenticate and whitelist the NTLM header.
  4. If only Basic auth is available, use a normal urllib3/requests pool with HTTPBasicAuth instead of the NTLM pool.

Example fix

// before
pool = NTLMConnectionPool(host='proxy.corp', username=user, password=pwd)  # server only does Negotiate
// after
import requests
resp = requests.get('http://proxy.corp/path', auth=(user, pwd))  # or configure NTLM on the proxy
Defensive patterns

Strategy: validation

Validate before calling

def server_offers_ntlm(host, port=80, path='/'):
    import http.client
    conn = http.client.HTTPConnection(host, port)
    conn.request('GET', path)
    hdr = conn.getresponse().getheader('WWW-Authenticate', '')
    return 'NTLM' in hdr

Type guard

def is_ntlm_challenge(www_authenticate_value):
    return isinstance(www_authenticate_value, str) and 'NTLM ' in www_authenticate_value

Try / catch

try:
    conn = ntlm_pool._new_conn()
except Exception as e:
    if 'Unexpected' in str(e) and 'response header' in str(e):
        log.error('Server did not answer with an NTLM challenge: %s', e)
        # fall back to basic auth or a plain pool
    else:
        raise

Prevention

When it happens

Trigger: Creating an NTLMConnectionPool and issuing a request when the server's WWW-Authenticate response does not include a 'NTLM <base64>' token — e.g. the server only offers Negotiate/Basic auth, sends a malformed header, or the pool is pointed at a non-NTLM endpoint.

Common situations: Pointing NTLMConnectionPool at a proxy or IIS server configured for Kerberos/Negotiate only; header mangled by a proxy that strips WWW-Authenticate; connecting to a plain HTTP service that does not speak NTLM at all.

Related errors


AI-assisted analysis of arduino/Arduino@a0df6e0e83 (2026-09-06). Data as JSON: /api/errors/574fed9f2e943c1f. Report an issue: GitHub.