AlistGo/alist · error

key with the same title already exists

Error message

key with the same title already exists

What it means

CreateSSHPublicKey enforces per-user title uniqueness: db.GetSSHPublicKeyByUserTitle(userId, title) succeeding means a key with that title already exists for this user, so creation is refused (the bool return is true, indicating the key was not created but no internal failure occurred). Titles are the user-facing handle for keys, so duplicates would be ambiguous.

Source

Thrown at internal/op/sshkey.go:14

package op

import (
	"github.com/alist-org/alist/v3/internal/db"
	"github.com/alist-org/alist/v3/internal/model"
	"github.com/pkg/errors"
	"golang.org/x/crypto/ssh"
	"time"
)

func CreateSSHPublicKey(k *model.SSHPublicKey) (error, bool) {
	_, err := db.GetSSHPublicKeyByUserTitle(k.UserId, k.Title)
	if err == nil {
		return errors.New("key with the same title already exists"), true
	}
	pubKey, _, _, _, err := ssh.ParseAuthorizedKey([]byte(k.KeyStr))
	if err != nil {
		return err, false
	}
	k.Fingerprint = ssh.FingerprintSHA256(pubKey)
	k.AddedTime = time.Now()
	k.LastUsedTime = k.AddedTime
	return db.CreateSSHPublicKey(k), true
}

func GetSSHPublicKeyByUserId(userId uint, pageIndex, pageSize int) (keys []model.SSHPublicKey, count int64, err error) {
	return db.GetSSHPublicKeyByUserId(userId, pageIndex, pageSize)
}

func GetSSHPublicKeyByIdAndUserId(id uint, userId uint) (*model.SSHPublicKey, error) {
	key, err := db.GetSSHPublicKeyById(id)
	if err != nil {

View on GitHub (pinned to 843d9dc814)

Solutions

  1. Delete or rename the existing key with that title first, then create the new one
  2. Use a distinct title (e.g. 'laptop-2026') for rotated keys
  3. Make setup scripts idempotent: check GetSSHPublicKeyByUserTitle before creating

Example fix

// before
err, created := op.CreateSSHPublicKey(&model.SSHPublicKey{UserId: uid, Title: "laptop", KeyStr: newKey})
// after: delete the stale entry with that title first
if _, err := db.GetSSHPublicKeyByUserTitle(uid, "laptop"); err == nil {
    _ = db.DeleteSSHPublicKeyByTitle(uid, "laptop") // or update its KeyStr
}
err, created := op.CreateSSHPublicKey(&model.SSHPublicKey{UserId: uid, Title: "laptop", KeyStr: newKey})
Defensive patterns

Strategy: validation

Validate before calling

// Before creating a key
if _, err := db.GetSSHPublicKeyByUserTitle(k.UserId, k.Title); err == nil {
    return errors.New("title in use; choose another or delete the old key")
}

Try / catch

if err, created := op.CreateSSHPublicKey(k); err != nil {
    if strings.Contains(err.Error(), "same title") {
        // prompt user to pick a new title or replace the existing key
    }
}

Prevention

When it happens

Trigger: Calling op.CreateSSHPublicKey with a title the same user already used for another key, regardless of the key material being different.

Common situations: Importing a rotated key under the old title (e.g. 'laptop') without deleting the previous entry; scripted key setup running twice; importing a keyset where titles collide.

Related errors


AI-assisted analysis of AlistGo/alist@843d9dc814 (2026-08-15). Data as JSON: /api/errors/84947e0a45039708. Report an issue: GitHub.