hyperledger/fabric · error

access denied, client identity wasn't supplied

Error message

access denied, client identity wasn't supplied

What it means

This error is returned by the discovery service's validateStructure when a client submits a discovery request whose Authentication section either has an empty ClientIdentity field or no Authentication at all is impossible — here specifically the Authentication object exists but ClientIdentity is empty. The discovery service requires the caller to identify itself so the server can authorize the request.

Solutions

  1. Populate req.Authentication.ClientIdentity with the client's serialized identity (e.g. x509 certificate bytes) before sending the discovery request
  2. Verify the client config/credential loading path actually returns the identity and is not silently empty
  3. Use a maintained SDK (e.g. fabric-sdk-go) that sets authentication automatically from the signing identity
  4. Test locally with TestValidateStructure to confirm the request passes structural validation before connecting

Example fix

// before
req := &discovery.Request{Authentication: &discovery.AuthInfo{}}
// after
req := &discovery.Request{Authentication: &discovery.AuthInfo{ClientIdentity: serializedIdentity}}
Defensive patterns

Strategy: validation

Validate before calling

func validateAuth(req *discovery.Request) error {
    if req.Authentication == nil || len(req.Authentication.ClientIdentity) == 0 {
        return errors.New("discovery request requires a non-empty ClientIdentity")
    }
    return nil
}

Type guard

func hasClientIdentity(req *discovery.Request) bool {
    return req != nil && req.Authentication != nil && len(req.Authentication.ClientIdentity) > 0
}

Try / catch

resp, err := client.Send(ctx, req)
if err != nil {
    if strings.Contains(err.Error(), "client identity wasn't supplied") {
        return fmt.Errorf("populating ClientIdentity and retrying: %w", reloadIdentityAndSend())
    }
    return err
}

Prevention

When it happens

Trigger: Calling the Discover or TestValidateStructure RPC with a request where req.Authentication is non-nil but req.Authentication.ClientIdentity is an empty byte slice.

Common situations: Client SDK misconfiguration where the identity/credential material (e.g. an enrollment certificate or serialized identity) failed to load silently; constructing a discovery.Request by hand without populating Auth; upgrading an SDK and losing the identity config field.

Understand the failure class

Related errors


AI-assisted analysis of hyperledger/fabric@2736b63f8f (2026-09-04). Data as JSON: /api/errors/c39b7f8e3bdb0d37. Report an issue: GitHub.

Appendix: source

Thrown at discovery/service.go:245

		}
	}
	return peersByOrg
}

// validateStructure validates that the request contains all the needed fields and that they are computed correctly
func validateStructure(ctx context.Context, request *discovery.SignedRequest, tlsEnabled bool, certHashFromContext certHashExtractor) (*discovery.Request, error) {
	if request == nil {
		return nil, errors.New("nil request")
	}
	req, err := protoext.SignedRequestToRequest(request)
	if err != nil {
		return nil, errors.Wrap(err, "failed parsing request")
	}
	if req.Authentication == nil {
		return nil, errors.New("access denied, no authentication info in request")
	}
	if len(req.Authentication.ClientIdentity) == 0 {
		return nil, errors.New("access denied, client identity wasn't supplied")
	}
	if !tlsEnabled {
		return req, nil
	}
	computedHash := certHashFromContext(ctx)
	if len(computedHash) == 0 {
		return nil, errors.New("client didn't send a TLS certificate")
	}
	if !bytes.Equal(computedHash, req.Authentication.ClientTlsCertHash) {
		claimed := hex.EncodeToString(req.Authentication.ClientTlsCertHash)
		logger.Warningf("client claimed TLS hash %s doesn't match computed TLS hash from gRPC stream %s", claimed, hex.EncodeToString(computedHash))
		return nil, errors.New("client claimed TLS hash doesn't match computed TLS hash from gRPC stream")
	}
	return req, nil
}

func validateCCQuery(ccQuery *discovery.ChaincodeQuery) error {
	if len(ccQuery.Interests) == 0 {

View on GitHub (pinned to 2736b63f8f)