henrygd/beszel · error

fingerprint mismatch

Error message

fingerprint mismatch

What it means

handleSingleRecord finds exactly one fingerprint record for the token. If the record has a fingerprint but it doesn't equal the fingerprint the connecting agent presents, the hub aborts with 'fingerprint mismatch' — the token belongs to a different machine, which could indicate token reuse or a cloned/rebuilt host.

Source

Thrown at internal/hub/agent_connect.go:215

	return acr.createNewSystemForUniversalToken(agentFingerprint)
}

// handleSingleRecord handles the case with a single fingerprint record. It validates
// the agent's fingerprint against the stored one, or sets it on first connect.
func (acr *agentConnectRequest) handleSingleRecord(fpRecord ws.FingerprintRecord, agentFingerprint common.FingerprintResponse) (ws.FingerprintRecord, error) {
	// If no current fingerprint, update with new fingerprint (first time connecting)
	if fpRecord.Fingerprint == "" {
		if err := acr.hub.SetFingerprint(&fpRecord, agentFingerprint.Fingerprint); err != nil {
			return fpRecord, err
		}
		// Update the record with the fingerprint that was set
		fpRecord.Fingerprint = agentFingerprint.Fingerprint
		return fpRecord, nil
	}

	// Abort if fingerprint exists but doesn't match (different machine)
	if fpRecord.Fingerprint != agentFingerprint.Fingerprint {
		return fpRecord, errors.New("fingerprint mismatch")
	}

	return fpRecord, nil
}

// handleMultipleRecordsOrUniversalToken finds a matching fingerprint from multiple records.
// If no match is found and the token is a universal token, a new system is created.
func (acr *agentConnectRequest) handleMultipleRecordsOrUniversalToken(fpRecords []ws.FingerprintRecord, agentFingerprint common.FingerprintResponse) (ws.FingerprintRecord, error) {
	// Return existing record with matching fingerprint if found
	for i := range fpRecords {
		if fpRecords[i].Fingerprint == agentFingerprint.Fingerprint {
			return fpRecords[i], nil
		}
	}

	// No matching fingerprint record found, but it's
	// an active universal token so create a new system
	if acr.isUniversalToken {

View on GitHub (pinned to b38fb7dafa)

Solutions

  1. Remove the system in the hub and re-add it so the new fingerprint is accepted with a fresh token
  2. If the fingerprint changed legitimately (container rebuilt), update/reset the stored fingerprint record
  3. Ensure the KEY is unique per system — never share one token across agents
  4. Check that a proxy isn't routing two different agents through one system entry

Example fix

// before: same KEY on two machines
KEY=<shared-key> on host-a and host-b
// after: unique key per system
host-a: KEY=<token-a>
host-b: KEY=<token-b>
Defensive patterns

Strategy: validation

Validate before calling

// ensure each host has its own key before deploy
if keyInUseOnAnotherHost(agentKey) {
    log.Fatal("KEY already bound to another system; generate a new one")
}

Try / catch

rec, err := acr.handleSingleRecord(fp)
if err != nil && err.Error() == "fingerprint mismatch" {
    // recreate container/hardware change: re-add the system in hub
    return reRegisterSystem()
}

Prevention

When it happens

Trigger: Agent connects; its token resolves to a single stored record whose Fingerprint differs from agentFingerprint.Fingerprint (e.g. agent reinstalled, container recreated, or key copied to a second machine).

Common situations: Copying the same KEY to multiple agents; recreating a Docker container so its generated fingerprint changed; disk reinstall or hardware change; agent moved to a different host without re-adding the system in the hub.

Related errors


AI-assisted analysis of henrygd/beszel@b38fb7dafa (2026-08-31). Data as JSON: /api/errors/a7139072178a340b. Report an issue: GitHub.