dagger/dagger · error

lookup cache for digests: hit missing shared result ID

Error message

lookup cache for digests: hit missing shared result ID

What it means

After a digest-based cache lookup finds a candidate hit, the result must have a shared result with a valid nonzero ID (its identity in the egraph/cache bookkeeping). If hitRes has no shared result or its id is 0, the cache's internal invariants are violated — the lookup matched a malformed entry — and the method returns this error while releasing the egraph lock.

Source

Thrown at dagql/cache.go:4652

		c.traceLookupMissNoMatch(ctx, recipeDigest.String(), false, -1, "", 0)
		c.egraphMu.Unlock()
		return nil, false, nil
	}

	hitRes.expiresAtUnix = mergeSharedResultExpiryUnix(
		hitRes.expiresAtUnix,
		candidateSharedResultExpiryUnix(nowUnix, 0),
	)
	touchSharedResultLastUsed(hitRes, now.UnixNano())
	retRes := Result[Typed]{
		shared:   hitRes,
		hitCache: true,
	}
	c.traceLookupHit(ctx, recipeDigest.String(), hitRes, match.termDigest)
	hitShared := retRes.cacheSharedResult()
	if hitShared == nil || hitShared.id == 0 {
		c.egraphMu.Unlock()
		return nil, false, fmt.Errorf("lookup cache for digests: hit missing shared result ID")
	}

	_, trackedCount, err := c.acquireSessionResultLocked(ctx, sessionID, hitShared)
	c.egraphMu.Unlock()
	if err != nil {
		return nil, false, err
	}

	loadedHit, err := c.ensurePersistedHitValueLoaded(ctx, resolver, retRes)
	if err != nil {
		return nil, false, err
	}
	if c.traceEnabled() {
		c.traceSessionResultTracked(ctx, sessionID, loadedHit, true, trackedCount)
	}
	return loadedHit, true, nil
}

View on GitHub (pinned to 82ba2681db)

Solutions

  1. Report/investigate as a cache invariant bug: find how a result without an ID became a lookup candidate (check the insert/publish path and any persistence rehydration code).
  2. Ensure all results are published through initCompletedResult / the normal sharedResult creation path that assigns ids.
  3. If results are rehydrated from persistence, verify IDs are restored or reassigned before the entries are added to digest indexes.
  4. As a workaround, clear/rebuild the affected cache state so malformed entries are dropped.

Example fix

// before (publisher that inserts a result without assigning an ID)
shared := &sharedResult{}
c.indexDigestLocked(recipeDigest, shared)
// after
shared := &sharedResult{}
shared.id = c.nextResultIDLocked() // assign identity before indexing
if shared == nil || shared.id == 0 { return fmt.Errorf("refusing to index result without ID") }
c.indexDigestLocked(recipeDigest, shared)
Defensive patterns

Strategy: type-guard

Type guard

func hasValidSharedResult(res dagql.AnyResult) bool {
    shared := res.CacheSharedResult()
    return shared != nil && shared.ID != 0
}

Try / catch

res, hit, err := cache.LookupCacheForDigests(ctx, sessionID, resolver, dg, extras)
if err != nil {
    if strings.Contains(err.Error(), "hit missing shared result ID") {
        // treat as a cache miss and re-execute the call
        return executeFresh(ctx, dg)
    }
    return err
}

Prevention

When it happens

Trigger: A digest-keyed lookup (Cache.lookupCacheForDigests) selects a candidate via selectLookupCandidateForSessionLocked whose shared result is nil or whose id was never assigned (0). This indicates corrupted or improperly published cache state rather than a caller mistake.

Common situations: A result was inserted into lookup structures without going through the normal publication path that assigns IDs; persistence/restore bugs where rehydrated results lack IDs; race or bug in result canonicalization leaving half-initialized shared results addressable by digest.

Related errors


AI-assisted analysis of dagger/dagger@82ba2681db (2026-09-05). Data as JSON: /api/errors/5696dfe938fb7479. Report an issue: GitHub.