unknwon/the-way-to-go_ZH_CN · error
key already exists
Error message
key already exists
What it means
Returned by URLStore.Set when the proposed short key is already present: Set takes the write lock, checks if _, present := s.urls[*key]; present, and refuses to overwrite, returning errors.New("key already exists"). By design, Put loops forever doing *key = genKey(s.Count()) until Set succeeds — so seeing this error escape means the retry loop was bypassed or Count() no longer reflects the stored keys.
Source
Thrown at eBook/19.8.md:68
*key = genKey(s.Count())
if err := s.Set(key, url); err == nil {
break
}
}
if s.save != nil {
s.save <- record{*key, *url}
}
return nil
}
```
`Put()` 调用 `Set()`,由于后者也要做调整,`key` 和 `url` 参数现在是指针类型,还必须返回 `error` 取代 `boolean`:
```go
func (s *URLStore) Set(key, url *string) error {
s.mu.Lock()
defer s.mu.Unlock()
if _, present := s.urls[*key]; present {
return errors.New("key already exists")
}
s.urls[*key] = *url
return nil
}
```
同样,当从 `load()` 调用 `Set()` 时,也必须做调整:
```go
s.Set(&r.Key, &r.URL)
```
还必须修改 HTTP 处理函数以适应 `URLStore` 上的更改。`Redirect()` 处理函数现在返回 `URLStore` 给出错误的字符串形式:
```go
func Redirect(w http.ResponseWriter, r *http.Request) {
key := r.URL.Path[1:]
var url string
if err := store.Get(&key, &url); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)View on GitHub (pinned to 7a54d34d36)
Solutions
- Never call Set directly from request handlers — always go through Put's for-loop, which re-derives *key = genKey(s.Count()) after every Set error and self-heals collisions
- After load(), sync the counter so genKey starts past existing keys: derive Count() from len(s.urls) or persist the counter alongside the records
- If your edit removed the loop around Set, restore it: for { *key = genKey(s.Count()); if err := s.Set(key, url); err == nil { break } }
- For long-lived stores, replace count-derived keys with a monotonic or random scheme so keys are never revisited
Example fix
// before: single attempt, collision escapes to the caller
if err := s.Set(&key, &url); err != nil {
return err // 'key already exists' reaches the HTTP layer
}
// after: book's design — regenerate until a free key is found
for {
*key = genKey(s.Count())
if err := s.Set(key, url); err == nil {
break
}
} Defensive patterns
Strategy: retry
Validate before calling
if _, present := urls[key]; present {
// pick the next candidate before calling Set
key = genKey(count + 1)
} Try / catch
for {
*key = genKey(s.Count())
if err := s.Set(key, url); err == nil {
break // key accepted
}
// 'key already exists': loop regenerates a fresh candidate
} Prevention
- Never call Set directly from request handlers; always go through Put's retry loop
- After load(), keep genKey's input (Count()) consistent with the stored records — persist or recompute the counter
- Make genKey injectable in tests and assert it produces distinct keys when Set reports collisions
- Prefer monotonic or random key schemes over count-derived keys for long-lived stores
When it happens
Trigger: load() replays a persisted data file into Set while genKey still derives keys from s.Count(), so regenerated keys collide with already-loaded ones; or a direct call to Set from handler code skips Put's regeneration loop; duplicated lines in the store file also make Count() drift from len(s.urls).
Common situations: Restarting the service with a populated data_store.txt; counter/len divergence after partial loads or failed saves; deleting entries without adjusting the counter; shortening genKey's alphabet so its cycle is exhausted and collisions become routine.
Related errors
- key not found
- err.Error()
- stack is empty
- I won't be able to do a sqrt of negative number!
- math - square root of negative number
AI-assisted analysis of unknwon/the-way-to-go_ZH_CN@7a54d34d36 (2026-08-15).
Data as JSON: /api/errors/2dd7e60dbd62eeb2.
Report an issue: GitHub.