{"record":{"id":"7acda18edaaa9361","repo":"juanfont/headscale","slug":"creating-key-in-database-w","errorCode":null,"errorMessage":"creating key in database: %w","messagePattern":"creating key in database: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"hscontrol/db/preauth_keys.go","lineNumber":130,"sourceCode":"\thash, err := bcrypt.GenerateFromPassword([]byte(toBeHashed), bcrypt.DefaultCost)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tkey := types.PreAuthKey{\n\t\tUserID:     userID, // nil for system-created keys, or \"created by\" for tagged keys\n\t\tUser:       user,   // nil for system-created keys\n\t\tReusable:   reusable,\n\t\tEphemeral:  ephemeral,\n\t\tCreatedAt:  &now,\n\t\tExpiration: expiration,\n\t\tTags:       aclTags, // empty for user-owned keys\n\t\tPrefix:     prefix,  // Store prefix\n\t\tHash:       hash,    // Store hash\n\t}\n\n\tif err := tx.Save(&key).Error; err != nil { //nolint:noinlineerr\n\t\treturn nil, fmt.Errorf(\"creating key in database: %w\", err)\n\t}\n\n\treturn &types.PreAuthKeyNew{\n\t\tID:         key.ID,\n\t\tKey:        keyStr,\n\t\tReusable:   key.Reusable,\n\t\tEphemeral:  key.Ephemeral,\n\t\tTags:       key.Tags,\n\t\tExpiration: key.Expiration,\n\t\tCreatedAt:  key.CreatedAt,\n\t\tUser:       key.User,\n\t}, nil\n}\n\n// SetPreAuthKeyDescription sets the free-text description on a pre-auth key.\n// The v2 keys API sets it after creation rather than threading it through the\n// many-armed CreatePreAuthKey signature shared by every other caller.\nfunc (hsdb *HSDatabase) SetPreAuthKeyDescription(id uint64, description string) error {","sourceCodeStart":112,"sourceCodeEnd":148,"githubUrl":"https://github.com/juanfont/headscale/blob/565fd254d06c4c7f9a8cad1714a43445c79ba420/hscontrol/db/preauth_keys.go#L112-L148","documentation":"GORM failed to persist a newly created pre-auth key during CreatePreAuthKeyCtx. The transaction's tx.Save on the PreAuthKey record returned an error, which is wrapped with this message. This is a database-layer failure, not a validation failure: the key string was already generated and hashed, but the row could not be written.","triggerScenarios":"Calling the pre-auth key creation path (db.CreatePreAuthKeyCtx / the CLI 'preauthkeys create' command / the gRPC CreatePreAuthKey API) when the underlying tx.Save fails: DB connection lost mid-transaction, unique-constraint violation on prefix, disk full, SQLite database locked, or a schema/migration mismatch where the prefix or hash column does not exist.","commonSituations":"Running an older binary against a newer schema (missing prefix/hash columns added by a migration), SQLite 'database is locked' under concurrent writers, PostgreSQL connection dropped, or a restored/corrupted database file. Note the caller must not retry key generation blindly: the key string was returned nowhere, but the row may or may not exist.","solutions":["Inspect the wrapped error chain (errors.Unwrap / %w output) — the GORM/driver message names the real cause (constraint, lock, missing column).","If the error mentions a missing column (prefix, hash), run migrations: ensure hscontrol/db migration code ran (headscale starts them automatically) and your binary version matches the schema.","For SQLite 'database is locked', reduce concurrent writers or check the busy_timeout setting in the sqlite config.","Verify database connectivity and permissions (path writable, Postgres up) and retry the create operation once the cause is fixed."],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"if err != nil {\n    var key *types.PreAuthKeyNew\n    _ = key\n    if errors.Is(err, gorm.ErrDuplicatedKey) {\n        // prefix collision (astronomically unlikely) or retry storm; regenerate and retry once\n    }\n    log.Error().Err(err).Msg(\"pre-auth key creation failed\")\n    return fmt.Errorf(\"create preauth key: %w\", err)\n}","preventionTips":["Keep the binary version and database schema in sync; run migrations before serving traffic.","For SQLite, set an adequate busy_timeout to avoid locked-database failures under concurrency.","Monitor DB health (connections, disk space) so Save failures surface before key issuance is attempted."],"tags":["database","gorm","preauth-key","sqlite","postgres"],"backgroundTag":null,"analyzedSha":"565fd254d06c4c7f9a8cad1714a43445c79ba420","analyzedAt":"2026-08-15T13:12:30.133Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}