golang/go · critical
tls: client's Finished message is incorrect
Error message
tls: client's Finished message is incorrect
What it means
The server recomputed the client's Finished verify_data from the master secret and transcript, and it did not match the value sent by the client. A wrong Finished message means the client's view of the handshake diverges from the server's — usually a sign of tampering, an attacker injecting messages, or a buggy peer.
Source
Thrown at src/crypto/tls/handshake_server.go:873
// finishedMsg is included in the transcript, but not until after we
// check the client version, since the state before this message was
// sent is used during verification.
msg, err := c.readHandshake(nil)
if err != nil {
return err
}
clientFinished, ok := msg.(*finishedMsg)
if !ok {
c.sendAlert(alertUnexpectedMessage)
return unexpectedMessageError(clientFinished, msg)
}
verify := hs.finishedHash.clientSum(hs.masterSecret)
if len(verify) != len(clientFinished.verifyData) ||
subtle.ConstantTimeCompare(verify, clientFinished.verifyData) != 1 {
c.sendAlert(alertHandshakeFailure)
return errors.New("tls: client's Finished message is incorrect")
}
if err := transcriptMsg(clientFinished, &hs.finishedHash); err != nil {
return err
}
copy(out, verify)
return nil
}
func (hs *serverHandshakeState) sendSessionTicket() error {
if !hs.hello.ticketSupported {
return nil
}
c := hs.c
m := new(newSessionTicketMsg)
View on GitHub (pinned to b6b368adc5)
Solutions
- Check for middleboxes, proxies, or TLS-terminating load balancers that might alter handshake bytes — they break the transcript.
- If using session resumption, ensure the ticket and PSK derivation are consistent between client and server.
- Verify both peers run compatible TLS implementations (avoid ancient versions on either side).
- Inspect cipher-suite negotiation for a mismatch that could cause divergent master-secret computation.
Defensive patterns
Strategy: try-catch
Validate before calling
// No caller-side validation possible; Finished mismatch is a peer/transcript // integrity problem. Ensure no middlebox alters handshake bytes.
Try / catch
// Server: treat as a potential MITM or bug; do not silently retry.
if err != nil && strings.Contains(err.Error(), "Finished message is incorrect") {
log.Error("Finished verification failed — possible tampering",
"remote", conn.RemoteAddr(), "err", err)
return err
} Prevention
- Audit all TLS-terminating intermediates for handshake-record alteration.
- Keep session-ticket/PSK derivation consistent across replicas.
- Update all peers to compatible TLS library versions.
When it happens
Trigger: In the server's finished-message processing: subtle.ConstantTimeCompare(verify, clientFinished.verifyData) != 1 (or length mismatch). The client computed verify_data with a different master secret or transcript hash.
Common situations: A MITM altering handshake records, an inconsistent master-secret derivation between client and server, a buggy client TLS library, or use of an incorrect PSK/session resumption. The handshake is aborted with handshake_failure.
Understand the failure class
- SSL/TLS and certificate errors — how TLS handshakes and certificate validation fail.
Related errors
- tls: client does not support uncompressed connections
- tls: initial handshake had non-empty renegotiation extension
- tls: FIPS 140-3 requires the use of Extended Master Secret
- tls: client certificate used with invalid signature algorith
- tls: invalid signature by the client certificate: {err}
AI-assisted analysis of golang/go@b6b368adc5 (2026-08-12).
Data as JSON: /api/errors/b5697c2c92c0dbb9.
Report an issue: GitHub.