Skip to content

Fix missing task cancellation and legacy keychain fingerprint mismatch#499

Open
nikhil8182 wants to merge 1 commit intosteipete:mainfrom
nikhil8182:fix/task-cancel-and-fingerprint-mismatch
Open

Fix missing task cancellation and legacy keychain fingerprint mismatch#499
nikhil8182 wants to merge 1 commit intosteipete:mainfrom
nikhil8182:fix/task-cancel-and-fingerprint-mismatch

Conversation

@nikhil8182
Copy link

Summary

  • Cancel pathDebugRefreshTask in UsageStore.deinit — this task was missing from the deinit cleanup, causing it to continue running after deallocation (the [weak self] prevents crashes but the task does unnecessary background work)
  • Fix legacy keychain fingerprint mismatch in ClaudeOAuthCredentials — two call sites (loadFromClaudeKeychainUsingSecurityFramework and syncFromClaudeKeychainWithoutPrompt) used currentClaudeKeychainFingerprintWithoutPrompt() after a successful legacy data read. This function prefers the newest candidate and may return a fingerprint for a different keychain item than the one actually read via the legacy service-level query. Now builds the fingerprint from claudeKeychainLegacyCandidateWithoutPrompt() directly, matching the pattern already used in the candidate path

Context

The fingerprint mismatch caused syncWithClaudeKeychainIfChanged to see a "changed" fingerprint on subsequent checks, triggering unnecessary keychain re-reads. This is related to #340 — while not the root cause (which is Claude Code's delete+recreate ACL wipe), it reduced the effectiveness of the cache layer that mitigates the repeated prompts.

Test plan

  • swift build passes cleanly
  • Verify pathDebugRefreshTask is properly canceled on store deallocation
  • Verify legacy keychain recovery path stores correct fingerprint, preventing unnecessary background sync re-reads

🤖 Generated with Claude Code

Cancel pathDebugRefreshTask in UsageStore deinit to prevent background
work continuing after deallocation.

Build legacy keychain fingerprints from the actual legacy candidate
instead of calling currentClaudeKeychainFingerprintWithoutPrompt(),
which prefers the newest candidate and may return a fingerprint for a
different keychain item. This caused stale comparisons in background
sync checks and unnecessary keychain re-reads.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Copy link
Collaborator

@ratulsarna ratulsarna left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A few questions to sanity-check this change.

{
// Same as above: store fingerprint after interactive read to avoid background "sync" reads.
self.saveClaudeKeychainFingerprint(self.currentClaudeKeychainFingerprintWithoutPrompt())
// Build fingerprint from the legacy candidate directly rather than using
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could saving the legacy candidate's fingerprint here get out of sync with the fingerprint that later change detection treats as current when duplicate keychain entries exist?

!creds.isExpired
{
self.saveClaudeKeychainFingerprint(self.currentClaudeKeychainFingerprintWithoutPrompt())
// Build fingerprint from the legacy candidate directly, matching the candidate
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could this fallback path end up with the same mismatch too, where we save one item's fingerprint but later background checks compare against another?

if let legacyCandidate = self.claudeKeychainLegacyCandidateWithoutPrompt(
promptMode: fallbackPromptMode)
{
let legacyFingerprint = ClaudeKeychainFingerprint(
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would it be worth pulling this fingerprint construction into a small helper? I'm wondering if that would also help with the file-length lint failure on this file.

@ratulsarna ratulsarna added the question Further information is requested label Mar 9, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

question Further information is requested

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants