From c9d55817f6ecdf049f75d0627d83af83a7d2a5fb Mon Sep 17 00:00:00 2001 From: Stephen Adamson Date: Mon, 3 Aug 2026 18:12:05 +0100 Subject: [PATCH] Fix data race on shared title-case transformer in profile updates golang.org/x/text/cases.Caser is not safe for concurrent use, but UpdateProfileHandler shared one package-level instance. A fresh caser is now created per call via titleCase(). --- backend/handlers/user/profile.go | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/backend/handlers/user/profile.go b/backend/handlers/user/profile.go index 2327f28..c7eb004 100644 --- a/backend/handlers/user/profile.go +++ b/backend/handlers/user/profile.go @@ -40,7 +40,14 @@ func getEnv(key, fallback string) string { return fallback } -var titleCaser = cases.Title(language.English) +// titleCase converts a name to Title Case. A fresh cases.Caser is created per +// call because golang.org/x/text/cases.Caser is NOT safe for concurrent use +// (it is a stateful transformer), and UpdateProfileHandler may run for two +// users at once. The package-level var previously shared one instance, which +// the race detector flagged (DATA RACE on titleCaser.String). +func titleCase(s string) string { + return cases.Title(language.English).String(strings.ToLower(s)) +} type UserProfile struct { ID string `json:"id"` @@ -297,8 +304,8 @@ func UpdateProfileHandler(w http.ResponseWriter, r *http.Request) { req.Phone = strings.TrimSpace(phone) // Title case names - req.FirstName = titleCaser.String(strings.ToLower(req.FirstName)) - req.LastName = titleCaser.String(strings.ToLower(req.LastName)) + req.FirstName = titleCase(req.FirstName) + req.LastName = titleCase(req.LastName) // Fetch user's current data for name change detection and CardDAV update var email string