slackhq/nebula · error
unable to login. error: %w
Error message
unable to login. error: %w
What it means
New() failed to login to the token with the provided PIN; the session is closed and the pkcs11 error is wrapped with %w. The library deliberately rethrows except for pkcs11 error 256 (CKR_USER_ALREADY_LOGGED_IN), which is tolerated. This means the token rejected authentication.
Source
Thrown at pkclient/pkclient_cgo.go:69
client := &PKClient{
module: module,
id: []byte(id),
label: []byte(label),
}
client.session, err = slots[slotIdx].OpenWriteSession()
if err != nil {
module.Destroy()
return nil, fmt.Errorf("failed to open session on slot %d", slotId)
}
if len(pin) != 0 {
err = client.session.Login(pin)
if err != nil {
// ignore "already logged in"
if !errors.Is(err, pkcs11.Error(256)) {
_ = client.session.Close()
return nil, fmt.Errorf("unable to login. error: %w", err)
}
}
}
// Make sure the hsm has a private key for deriving
client.privKeyObj, err = client.findDeriveKey(client.id, client.label, true)
if err != nil {
_ = client.Close() //log out, close session, destroy module
return nil, fmt.Errorf("failed to find private key for deriving: %w", err)
}
return client, nil
}
// Close cleans up properly and logs out
func (c *PKClient) Close() error {
var err error = nil
if c.session != nil {View on GitHub (pinned to dd8f660c0a)
Solutions
- Verify the PIN is correct for the selected token/slot
- If the PIN is locked, unlock/reset it with the vendor tool (or wait per policy)
- If no auth is needed, pass an empty pin so Login is skipped
- Confirm slotId points at the token the PIN belongs to
- Test the PIN independently with pkcs11-tool --login --pin
Example fix
// before pkcs11_pin: "1234" // after pkcs11_pin: "correct-current-pin"
Defensive patterns
Strategy: try-catch
Validate before calling
// verify the PIN by logging in on a scratch session before New()
ctx := pkcs11.New(modulePath); ctx.Initialize()
session, _ := ctx.OpenSession(slot, pkcs11.CKF_SERIAL_SESSION|pkcs11.CKF_RW_SESSION)
if err := ctx.Login(session, pkcs11.CKU_USER, pin); err != nil {
return fmt.Errorf("PIN rejected: %w", err)
} Try / catch
client, err := pkclient.New(hsmPath, slot, pin, id, label)
if err != nil {
var p11err pkcs11.Error
if errors.As(err, &p11err) && uint(p11err) == 0xA0+0x20 { // CKR_PIN_LOCKED (163/0x000000A0 family)
return errors.New("PIN locked; unlock with vendor tool before retrying")
}
if strings.Contains(err.Error(), "unable to login") {
return fmt.Errorf("check PIN for slot %d: %w", slot, err)
}
return err
} Prevention
- Store the PIN in a secret manager, not plaintext config
- Verify the PIN with pkcs11-tool --login before deploy
- Handle CKR_PIN_LOCKED by resetting, not retrying
- Use an empty pin only when the token genuinely needs no login
- Confirm the PIN belongs to the selected slot's token
When it happens
Trigger: Calling New() with a non-empty pin when session.Login(pin) returns an error other than pkcs11.Error(256): wrong PIN, CKR_PIN_LOCKED after repeated failures, CKR_USER_PIN_NOT_INITIALIZED, or login to a slot with no user credentials.
Common situations: Expired or mistyped PIN in config; account locked from previous attempts; PIN configured for a different token; HSM requiring SO login first.
Related errors
- notImplemented
- error while creating PKCS#11 client: %w
- error while getting public key with PKCS#11: %w
- error while signing with PKCS#11: %w
- error while creating PKCS#11 client: %w
AI-assisted analysis of slackhq/nebula@dd8f660c0a (2026-09-03).
Data as JSON: /api/errors/d39904663a9fe19a.
Report an issue: GitHub.