{"record":{"id":"cc887d851f79a597","repo":"gastownhall/beads","slug":"no-cached-clone-for-s-run-ensure-first","errorCode":null,"errorMessage":"no cached clone for %s — run Ensure first","messagePattern":"no cached clone for (.+?) — run Ensure first","errorType":"console","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/remotecache/cache.go","lineNumber":173,"sourceCode":"\tmeta := c.readMeta(remoteURL)\n\tmeta.LastPush = time.Now().UnixNano()\n\tc.writeMeta(remoteURL, meta)\n\n\treturn nil\n}\n\n// OpenStore opens a DoltStorage from the cached clone using the provided\n// StoreOpener. The cache entry directory is used as the beads directory.\n// The caller is responsible for calling Close() on the returned store.\n//\n// Note: OpenStore does not acquire a cache lock. The caller must ensure\n// no concurrent Ensure() or Push() is running against the same remoteURL,\n// as those modify the underlying dolt database. This is safe for single-\n// process CLI use but not for concurrent multi-process access.\nfunc (c *Cache) OpenStore(ctx context.Context, remoteURL string, opener StoreOpener) (storage.DoltStorage, error) {\n\tentry := c.entryDir(remoteURL)\n\tif !c.doltExists(c.cloneTarget(remoteURL)) {\n\t\treturn nil, fmt.Errorf(\"no cached clone for %s — run Ensure first\", remoteURL)\n\t}\n\treturn opener(ctx, entry)\n}\n\n// Evict removes a cached remote clone entirely.\nfunc (c *Cache) Evict(remoteURL string) error {\n\tentry := c.entryDir(remoteURL)\n\treturn os.RemoveAll(entry)\n}\n\n// doltExists checks if a dolt database exists at the given path.\nfunc (c *Cache) doltExists(dbPath string) bool {\n\tdoltDir := filepath.Join(dbPath, \".dolt\")\n\tinfo, err := os.Stat(doltDir)\n\treturn err == nil && info.IsDir()\n}\n\n// doltClone clones a remote into the target directory.","sourceCodeStart":155,"sourceCodeEnd":191,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/internal/remotecache/cache.go#L155-L191","documentation":"OpenStore() checks that a dolt database (.dolt directory) exists in the cache entry for the remote URL before opening it. This error means the cache was never populated — Ensure() has not successfully cloned (or the entry was evicted/deleted) — so there is no local clone to open a store from.","triggerScenarios":"Calling Cache.OpenStore(ctx, remoteURL, opener) without a prior successful Cache.Ensure(ctx, remoteURL); after Cache.Evict() removed the entry; after a failed clone left no .dolt dir; or the user deleted the cache directory (~/.cache/beads/remotes/...).","commonSituations":"Fresh machine or CI runner where Ensure() was skipped; disk cleanup wiped the cache; calling OpenStore in a custom workflow that only ever calls Push(); clone previously failed silently.","solutions":["Call Cache.Ensure(ctx, remoteURL) before OpenStore — this clones or refreshes the cache entry.","Check that Ensure() returned no error and that <entry>/<db>/.dolt exists on disk.","If the entry should exist, verify you are passing the exact same remoteURL string (CacheKey is a hash of the URL; any difference maps to a different entry).","Run `bd` commands normally (sync/pull) which handle Ensure internally before opening the store."],"exampleFix":"// before: open directly, crashes with \"no cached clone\"\nstore, err := cache.OpenStore(ctx, remoteURL, opener)\n// after: ensure first\nif _, err := cache.Ensure(ctx, remoteURL); err != nil { return err }\nstore, err := cache.OpenStore(ctx, remoteURL, opener)","handlingStrategy":"validation","validationCode":"entryDir := filepath.Join(cacheDir, \"beads\", \"remotes\", remotecache.CacheKey(remoteURL))\nif _, err := os.Stat(filepath.Join(entryDir, \"beads\", \".dolt\")); os.IsNotExist(err) {\n    if _, err := cache.Ensure(ctx, remoteURL); err != nil {\n        return fmt.Errorf(\"cache not populated: %w\", err)\n    }\n}","typeGuard":null,"tryCatchPattern":"store, err := cache.OpenStore(ctx, url, opener)\nif err != nil && strings.Contains(err.Error(), \"no cached clone\") {\n    if _, err := cache.Ensure(ctx, url); err != nil { return err }\n    store, err = cache.OpenStore(ctx, url, opener)\n}\nif err != nil { return err }\ndefer store.Close()","preventionTips":["Always pair OpenStore with a prior Ensure() in your workflow.","Don't Evict() entries that later code paths will open without re-Ensuring.","Use the exact same remoteURL string everywhere (CacheKey hashes the URL — variations create different entries).","Remember cache dirs can be wiped by disk cleanup; code must tolerate a cold cache."],"tags":["go","cache","dolt","missing-state"],"backgroundTag":"cache-miss-ensure-first","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}