canopy-network/canopy · error

nickname already used

Error message

nickname already used

What it means

Keystore.Import returns "nickname already used" when importing a key with an ImportOptions.Nickname that is already mapped to a different address in the keystore's NicknameMap. Each nickname must uniquely identify one address to keep lookups unambiguous.

Source

Thrown at lib/crypto/keystore.go:97

func (ks *Keystore) Import(encrypted *EncryptedPrivateKey, opts ImportOpts) error {
	// TODO: better naming
	encrypted.KeyAddress = hex.EncodeToString(opts.Address)
	encrypted.KeyNickname = opts.Nickname

	// this is needed to prevent saving various nicknames for the same address
	var oldNickname string
	oldKey := ks.AddressMap[encrypted.KeyAddress]
	if oldKey != nil {
		oldNickname = oldKey.KeyNickname
	}
	if oldNickname != "" {
		delete(ks.NicknameMap, oldNickname)
	}

	if opts.Nickname != "" {
		_, ok := ks.NicknameMap[opts.Nickname]
		if ok && opts.Nickname != oldNickname {
			return errors.New("nickname already used")
		}
		ks.NicknameMap[opts.Nickname] = encrypted.KeyAddress
	}

	ks.AddressMap[encrypted.KeyAddress] = encrypted

	return nil
}

type ImportRawOpts struct {
	Nickname string
}

// ImportRaw() imports a non-encrypted private key to the store, but encrypts it given a password
func (ks *Keystore) ImportRaw(privateKeyBytes []byte, password string, opts ImportRawOpts) (address string, err error) {
	if password == "" {
		return "", fmt.Errorf("invalid password")
	}

View on GitHub (pinned to ee8197d91d)

Solutions

  1. Pick a unique nickname or generate one (suffix/timestamp) before importing
  2. Omit opts.Nickname to import without a nickname
  3. If re-importing the same key intentionally, clear/replace the old nickname mapping first

Example fix

// before
_, err := ks.ImportRaw(pk, &keystore.ImportOptions{Nickname: "validator"}) // second import
// after
if _, exists := ks.NicknameMap["validator"]; exists {
	opts.Nickname = "validator-2"
}
_, err := ks.ImportRaw(pk, opts)
Defensive patterns

Strategy: try-catch

Validate before calling

if opts.Nickname != "" {
	if addr, ok := ks.NicknameMap[opts.Nickname]; ok && addr != encrypted.KeyAddress {
		opts.Nickname = opts.Nickname + "-" + fmt.Sprintf("%d", time.Now().Unix())
	}
}

Try / catch

_, err := ks.ImportRaw(pk, opts)
if err != nil && strings.Contains(err.Error(), "nickname already used") {
	opts.Nickname = uniquify(opts.Nickname)
	_, err = ks.ImportRaw(pk, opts)
}

Prevention

When it happens

Trigger: Import/ImportRaw called twice with the same opts.Nickname for two different keys; renaming a key to a nickname owned by another key.

Common situations: Re-running an import script that reuses a fixed nickname; exporting/importing across keystores where a nickname already exists; automation that doesn't detect existing nicknames.

Understand the failure class

Background: "already exists" / EEXIST / FileAlreadyExistsException: what the 'file already exists' error means and how to fix it — this error's family across 37 libraries.


AI-assisted analysis of canopy-network/canopy@ee8197d91d (2026-09-06). Data as JSON: /api/errors/985da1392791de56. Report an issue: GitHub.