Skip to content

fix(desktop): prevent IPC ID collisions with global operation ID counter#712

Merged
Kitenite merged 4 commits intomainfrom
fix/desktop-trpc-client-id-collision
Jan 12, 2026
Merged

fix(desktop): prevent IPC ID collisions with global operation ID counter#712
Kitenite merged 4 commits intomainfrom
fix/desktop-trpc-client-id-collision

Conversation

@Kitenite
Copy link
Copy Markdown
Collaborator

@Kitenite Kitenite commented Jan 12, 2026

Summary

Fixes bug where auth subscription received incorrect data ({success: true}) from mutation responses due to IPC operation ID collisions.

Root Cause

The app has two tRPC clients:

  • React client (reactClient) - for hooks via TRPCProvider
  • Proxy client (trpcClient) - for imperative calls from stores/utilities

Each tRPC client calls the ipcLink factory during initialization, creating separate IPCClient instances. Each IPCClient:

  1. Registers its own onMessage handler (all handlers receive all IPC responses)
  2. Has its own #pendingRequests map keyed by operation ID
  3. Generates IDs independently (1, 2, 3...)

When both clients have operations with the same ID, responses can be routed to the wrong client.

Solution

Add sessionIdLink that replaces operation IDs with globally unique values from a shared counter. This ensures all operations across both clients get distinct IDs.

Also Fixes

  • AuthSession type now correctly extracts session data from API response (was including the full {data, error} wrapper)
  • Exports explicit AuthState type for subscription output

Test plan

  • Restart desktop app dev server
  • Rapidly refresh the renderer (Cmd+R multiple times)
  • Verify auth state no longer receives {success: true} or other invalid shapes
  • Verify normal auth flow works (sign in, sign out, organization switching)

🤖 Generated with Claude Code


Note

Prevents IPC response misrouting between multiple tRPC clients by ensuring globally unique operation IDs.

  • Introduces sessionIdLink that replaces op.id with a global counter and applies it to both reactClient and trpcClient
  • TRPCProvider now uses reactClient created in renderer/lib/trpc-client
  • Refines auth typing: exports AuthSession, adds AuthState for onAuthState, and updates AuthProvider to use the new types

Written by Cursor Bugbot for commit 39543c0. This will update automatically on new commits. Configure here.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Jan 12, 2026

📝 Walkthrough

Walkthrough

Adds exported auth types (AuthSession, AuthState), updates AuthProvider to use the new AuthState type, and centralizes tRPC clients by introducing a React-specific reactClient, adding sessionIdLink to prevent IPC operation-id collisions, and switching TRPCProvider to use the shared client.

Changes

Cohort / File(s) Summary
Auth router types
apps/desktop/src/lib/trpc/routers/auth/index.ts
Exported a new public AuthState type and changed the onAuthState emission typing to use it.
Auth implementation & re-exports
apps/desktop/src/main/lib/auth/auth.ts, apps/desktop/src/main/lib/auth/index.ts
Introduced SessionResponse alias and public AuthSession type; re-exported AuthSession from the auth index.
Auth context wiring
apps/desktop/src/renderer/contexts/AuthProvider/AuthProvider.tsx
Added local AuthState alias; updated AuthContextValue.session typing and inlined token/session extraction from the auth observable.
TRPC provider update
apps/desktop/src/renderer/contexts/TRPCProvider/TRPCProvider.tsx
Replaced a locally-created TRPC client with the shared reactClient import; removed local ipcLink/superjson setup.
Shared TRPC client & session id link
apps/desktop/src/renderer/lib/trpc-client.ts, apps/desktop/src/renderer/lib/session-id-link.ts
Added reactClient (React hooks client) and updated trpcClient to include sessionIdLink(); introduced sessionIdLink() that assigns globally unique operation IDs to avoid cross-client IPC routing collisions.

Sequence Diagram(s)

sequenceDiagram
    participant UI as React Component
    participant ReactClient as reactClient (trpc)
    participant SessionLink as sessionIdLink
    participant Ipc as ipcLink / IPC
    participant Main as Main Process / trpc server
    UI->>ReactClient: invoke query/mutation (hooks)
    ReactClient->>SessionLink: attach global operation id
    SessionLink->>Ipc: forward request with id
    Ipc->>Main: send IPC message
    Main->>Main: route to appropriate router (e.g., auth)
    Main-->>Ipc: send response
    Ipc-->>SessionLink: deliver response (matches by id)
    SessionLink-->>ReactClient: forward response
    ReactClient-->>UI: update hook state
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

  • New desktop UI #102 — Prior TRPC client/provider bootstrap and TRPC wiring that this change refactors and extends.

Poem

🐰
I hop between the client and link,
assign each call a global wink,
auth types snug in tidy rows,
one shared client so the data flows,
I munch a carrot, ship — and blink.

🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 33.33% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately describes the primary fix in the PR—preventing IPC ID collisions via a global operation ID counter.
Description check ✅ Passed The PR description provides a clear summary, root cause analysis, solution, related changes, and a detailed test plan addressing the bug fix.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In @apps/desktop/src/renderer/lib/trpc-client.ts:
- Around line 1-4: Remove the unused AppRouter type import from the top-level
import list: delete "AppRouter" from the import statement so only the actually
used modules (superjson, ipcLink, trpc) are imported; verify there are no other
references to AppRouter in this file (the trpc instance from ./trpc already
carries the type), then run a quick TypeScript/ESLint check to ensure the unused
import warning is resolved.
🧹 Nitpick comments (3)
apps/desktop/src/renderer/contexts/TRPCProvider/TRPCProvider.tsx (1)

1-4: Consider using alias import for consistency.

Line 2 uses the alias lib/trpc while line 4 uses a relative path ../../lib/trpc-client. For consistency and per coding guidelines, consider using the alias if configured.

-import { trpcClient } from "../../lib/trpc-client";
+import { trpcClient } from "lib/trpc-client";
apps/desktop/src/main/lib/auth/auth.ts (1)

227-229: Consider logging the error instead of silently swallowing it.

Per coding guidelines, errors should not be swallowed silently. While it's understandable that the file may not exist, logging at debug level would aid troubleshooting.

 		try {
 			await fs.unlink(TOKEN_FILE);
-		} catch {}
+		} catch (error) {
+			// File may not exist, which is fine during signOut
+			console.log("[auth] Token file cleanup:", error instanceof Error ? error.message : "unknown error");
+		}
apps/desktop/src/renderer/contexts/AuthProvider/AuthProvider.tsx (1)

5-6: Consider importing AuthState from the canonical source.

The AuthState type is already exported from apps/desktop/src/lib/trpc/routers/auth/index.ts. Importing it directly would ensure type consistency and avoid potential drift if the router type changes.

♻️ Suggested refactor
 import { createContext, type ReactNode, useContext } from "react";
-import type { RouterOutputs } from "../../lib/trpc";
+import type { AuthState } from "@/lib/trpc/routers/auth";
 import { trpc } from "../../lib/trpc";
-
-type AuthState = RouterOutputs["auth"]["onAuthState"];
📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 64987a8 and b9a6d5b.

📒 Files selected for processing (6)
  • apps/desktop/src/lib/trpc/routers/auth/index.ts
  • apps/desktop/src/main/lib/auth/auth.ts
  • apps/desktop/src/main/lib/auth/index.ts
  • apps/desktop/src/renderer/contexts/AuthProvider/AuthProvider.tsx
  • apps/desktop/src/renderer/contexts/TRPCProvider/TRPCProvider.tsx
  • apps/desktop/src/renderer/lib/trpc-client.ts
🧰 Additional context used
📓 Path-based instructions (6)
apps/desktop/**/*.{ts,tsx}

📄 CodeRabbit inference engine (apps/desktop/AGENTS.md)

apps/desktop/**/*.{ts,tsx}: For Electron interprocess communication, ALWAYS use tRPC as defined in src/lib/trpc
Use alias as defined in tsconfig.json when possible
Prefer zustand for state management if it makes sense. Do not use effect unless absolutely necessary.
For tRPC subscriptions with trpc-electron, ALWAYS use the observable pattern from @trpc/server/observable instead of async generators, as the library explicitly checks isObservable(result) and throws an error otherwise

Files:

  • apps/desktop/src/lib/trpc/routers/auth/index.ts
  • apps/desktop/src/renderer/contexts/AuthProvider/AuthProvider.tsx
  • apps/desktop/src/renderer/contexts/TRPCProvider/TRPCProvider.tsx
  • apps/desktop/src/main/lib/auth/auth.ts
  • apps/desktop/src/main/lib/auth/index.ts
  • apps/desktop/src/renderer/lib/trpc-client.ts
**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{ts,tsx}: Use object parameters for functions with 2+ parameters instead of positional arguments
Functions with 2+ parameters should accept a single params object with named properties for self-documentation and extensibility
Use prefixed console logging with context pattern: [domain/operation] message
Extract magic numbers and hardcoded values to named constants at module top
Use lookup objects/maps instead of repeated if (type === ...) conditionals
Avoid using any type - maintain type safety in TypeScript code
Never swallow errors silently - at minimum log them with context
Import from concrete files directly when possible - avoid barrel file abuse that creates circular dependencies
Avoid deep nesting (4+ levels) - use early returns, extract functions, and invert conditions
Use named properties in options objects instead of boolean parameters to avoid boolean blindness

Files:

  • apps/desktop/src/lib/trpc/routers/auth/index.ts
  • apps/desktop/src/renderer/contexts/AuthProvider/AuthProvider.tsx
  • apps/desktop/src/renderer/contexts/TRPCProvider/TRPCProvider.tsx
  • apps/desktop/src/main/lib/auth/auth.ts
  • apps/desktop/src/main/lib/auth/index.ts
  • apps/desktop/src/renderer/lib/trpc-client.ts
apps/**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

Use Drizzle ORM for all database operations - never use raw SQL

Files:

  • apps/desktop/src/lib/trpc/routers/auth/index.ts
  • apps/desktop/src/renderer/contexts/AuthProvider/AuthProvider.tsx
  • apps/desktop/src/renderer/contexts/TRPCProvider/TRPCProvider.tsx
  • apps/desktop/src/main/lib/auth/auth.ts
  • apps/desktop/src/main/lib/auth/index.ts
  • apps/desktop/src/renderer/lib/trpc-client.ts
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (AGENTS.md)

Use Biome for formatting and linting - run at root level with bun run lint:fix or biome check --write

Files:

  • apps/desktop/src/lib/trpc/routers/auth/index.ts
  • apps/desktop/src/renderer/contexts/AuthProvider/AuthProvider.tsx
  • apps/desktop/src/renderer/contexts/TRPCProvider/TRPCProvider.tsx
  • apps/desktop/src/main/lib/auth/auth.ts
  • apps/desktop/src/main/lib/auth/index.ts
  • apps/desktop/src/renderer/lib/trpc-client.ts
apps/desktop/src/renderer/**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

Never import Node.js modules (fs, path, os, net) in renderer process or shared code - they are externalized for browser compatibility

Files:

  • apps/desktop/src/renderer/contexts/AuthProvider/AuthProvider.tsx
  • apps/desktop/src/renderer/contexts/TRPCProvider/TRPCProvider.tsx
  • apps/desktop/src/renderer/lib/trpc-client.ts
**/*.tsx

📄 CodeRabbit inference engine (AGENTS.md)

One component per file - do not create multi-component files

Files:

  • apps/desktop/src/renderer/contexts/AuthProvider/AuthProvider.tsx
  • apps/desktop/src/renderer/contexts/TRPCProvider/TRPCProvider.tsx
🧠 Learnings (10)
📓 Common learnings
Learnt from: CR
Repo: superset-sh/superset PR: 0
File: apps/desktop/AGENTS.md:0-0
Timestamp: 2025-12-21T04:39:28.543Z
Learning: Applies to apps/desktop/**/*.{ts,tsx} : For Electron interprocess communication, ALWAYS use tRPC as defined in `src/lib/trpc`
📚 Learning: 2025-12-21T04:39:28.543Z
Learnt from: CR
Repo: superset-sh/superset PR: 0
File: apps/desktop/AGENTS.md:0-0
Timestamp: 2025-12-21T04:39:28.543Z
Learning: Applies to apps/desktop/**/*.{ts,tsx} : Prefer zustand for state management if it makes sense. Do not use effect unless absolutely necessary.

Applied to files:

  • apps/desktop/src/renderer/contexts/AuthProvider/AuthProvider.tsx
📚 Learning: 2025-12-21T04:39:28.543Z
Learnt from: CR
Repo: superset-sh/superset PR: 0
File: apps/desktop/AGENTS.md:0-0
Timestamp: 2025-12-21T04:39:28.543Z
Learning: Applies to apps/desktop/**/*.{ts,tsx} : For Electron interprocess communication, ALWAYS use tRPC as defined in `src/lib/trpc`

Applied to files:

  • apps/desktop/src/renderer/contexts/TRPCProvider/TRPCProvider.tsx
  • apps/desktop/src/renderer/lib/trpc-client.ts
📚 Learning: 2026-01-02T06:50:28.671Z
Learnt from: CR
Repo: superset-sh/superset PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-02T06:50:28.671Z
Learning: Applies to apps/api/**/*.{ts,tsx} : Use TRPCError with appropriate error codes: NOT_FOUND, UNAUTHORIZED, FORBIDDEN, BAD_REQUEST, INTERNAL_SERVER_ERROR, NOT_IMPLEMENTED

Applied to files:

  • apps/desktop/src/renderer/contexts/TRPCProvider/TRPCProvider.tsx
  • apps/desktop/src/renderer/lib/trpc-client.ts
📚 Learning: 2025-12-21T04:39:28.543Z
Learnt from: CR
Repo: superset-sh/superset PR: 0
File: apps/desktop/AGENTS.md:0-0
Timestamp: 2025-12-21T04:39:28.543Z
Learning: Applies to apps/desktop/**/*.{ts,tsx} : For tRPC subscriptions with trpc-electron, ALWAYS use the observable pattern from `trpc/server/observable` instead of async generators, as the library explicitly checks `isObservable(result)` and throws an error otherwise

Applied to files:

  • apps/desktop/src/renderer/contexts/TRPCProvider/TRPCProvider.tsx
  • apps/desktop/src/renderer/lib/trpc-client.ts
📚 Learning: 2026-01-02T06:50:28.671Z
Learnt from: CR
Repo: superset-sh/superset PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-02T06:50:28.671Z
Learning: Applies to apps/api/**/*.{ts,tsx} : Extract business logic from tRPC procedures into utility functions when logic exceeds ~50 lines, is used by multiple procedures, or needs independent testing

Applied to files:

  • apps/desktop/src/renderer/contexts/TRPCProvider/TRPCProvider.tsx
  • apps/desktop/src/renderer/lib/trpc-client.ts
📚 Learning: 2026-01-02T06:50:28.671Z
Learnt from: CR
Repo: superset-sh/superset PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-02T06:50:28.671Z
Learning: Applies to apps/desktop/src/renderer/**/*.{ts,tsx} : Never import Node.js modules (fs, path, os, net) in renderer process or shared code - they are externalized for browser compatibility

Applied to files:

  • apps/desktop/src/renderer/contexts/TRPCProvider/TRPCProvider.tsx
  • apps/desktop/src/renderer/lib/trpc-client.ts
📚 Learning: 2025-12-21T04:39:28.543Z
Learnt from: CR
Repo: superset-sh/superset PR: 0
File: apps/desktop/AGENTS.md:0-0
Timestamp: 2025-12-21T04:39:28.543Z
Learning: Applies to apps/desktop/**/*.{ts,tsx} : Use alias as defined in `tsconfig.json` when possible

Applied to files:

  • apps/desktop/src/main/lib/auth/auth.ts
  • apps/desktop/src/main/lib/auth/index.ts
📚 Learning: 2026-01-02T06:50:28.671Z
Learnt from: CR
Repo: superset-sh/superset PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-02T06:50:28.671Z
Learning: Applies to apps/api/**/*.{ts,tsx} : tRPC procedures and API route handlers should validate and delegate; keep orchestrators thin

Applied to files:

  • apps/desktop/src/renderer/lib/trpc-client.ts
📚 Learning: 2026-01-02T06:50:28.671Z
Learnt from: CR
Repo: superset-sh/superset PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-02T06:50:28.671Z
Learning: Applies to apps/desktop/src/lib/*.ts : Never import Node.js modules in shared code like electron-router-dom.ts - it runs in both main and renderer processes

Applied to files:

  • apps/desktop/src/renderer/lib/trpc-client.ts
🧬 Code graph analysis (2)
apps/desktop/src/lib/trpc/routers/auth/index.ts (3)
apps/desktop/src/main/lib/auth/auth.ts (1)
  • AuthSession (50-50)
apps/desktop/src/lib/trpc/index.ts (1)
  • router (47-47)
packages/trpc/src/index.ts (1)
  • publicProcedure (12-12)
apps/desktop/src/renderer/contexts/AuthProvider/AuthProvider.tsx (1)
apps/desktop/src/lib/trpc/routers/auth/index.ts (1)
  • AuthState (8-8)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: Cursor Bugbot
  • GitHub Check: Build
🔇 Additional comments (7)
apps/desktop/src/renderer/contexts/TRPCProvider/TRPCProvider.tsx (1)

23-27: LGTM! Correctly uses the shared tRPC client.

This change properly addresses the IPC ID collision issue by using the shared trpcClient instance instead of creating a separate client. The single client ensures consistent ID counters across all tRPC operations.

apps/desktop/src/main/lib/auth/index.ts (1)

1-2: LGTM! Clean type re-export.

The addition of AuthSession to the public API surface correctly exposes the session type needed by the auth router and other consumers.

apps/desktop/src/lib/trpc/routers/auth/index.ts (2)

7-8: LGTM! Well-defined auth state type.

The AuthState type correctly captures the union of authenticated state (session + token) and unauthenticated state (null). The JSDoc comment provides helpful context.


12-42: LGTM! Subscription correctly typed and implemented.

The observable pattern is correctly used per coding guidelines for trpc-electron. The AuthState generic ensures subscribers receive properly typed data, addressing the original bug where auth subscriptions received incorrect shapes like {success: true}.

apps/desktop/src/renderer/lib/trpc-client.ts (1)

6-19: LGTM! Well-documented single client implementation.

The change correctly consolidates tRPC client creation to prevent IPC ID collisions. The documentation clearly explains the rationale and usage patterns. Using trpc.createClient (from the typed createTRPCReact instance) ensures compatibility with both React hooks and imperative calls.

apps/desktop/src/main/lib/auth/auth.ts (1)

48-51: LGTM! Correct type extraction from API response.

The type chain properly extracts session data:

  1. SessionResponse captures the full {data, error} response
  2. AuthSession extracts only the data property, excluding null
  3. Session provides a local alias

This correctly addresses the bug where AuthSession previously included the full wrapper.

apps/desktop/src/renderer/contexts/AuthProvider/AuthProvider.tsx (1)

14-23: LGTM! Clean simplification of the auth context provider.

The inline value computation with optional chaining and nullish coalescing correctly handles the undefined state before the subscription emits. The type alignment with AuthState properly reflects that the session can be null, fixing the type extraction issue mentioned in the PR objectives.

Comment thread apps/desktop/src/renderer/lib/trpc-client.ts
The app has two tRPC clients:
- React client (for hooks via TRPCProvider)
- Proxy client (for imperative calls from stores/utilities)

Each ipcLink creates its own IPCClient with a separate ID counter.
When both clients used separate ipcLinks, operations could get the same
ID, causing responses to be misrouted (e.g., mutation response
{success: true} going to a subscription).

Fix: Create a single shared ipcLink instance used by both clients.
This ensures all operations get unique IDs from the same counter.

Also fixes AuthSession type to correctly extract session data from
the API response (was including the full {data, error} wrapper).

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@Kitenite Kitenite force-pushed the fix/desktop-trpc-client-id-collision branch from b9a6d5b to fccc6c7 Compare January 12, 2026 02:13
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (1)
apps/desktop/src/renderer/contexts/AuthProvider/AuthProvider.tsx (1)

5-6: Consider importing AuthState directly from the router.

The local type alias duplicates the AuthState type already exported from apps/desktop/src/lib/trpc/routers/auth/index.ts. While using RouterOutputs is idiomatic for tRPC and will stay in sync, importing the explicit type could improve clarity and reduce duplication.

♻️ Optional: Import AuthState directly
 import { createContext, type ReactNode, useContext } from "react";
-import type { RouterOutputs } from "../../lib/trpc";
+import type { AuthState } from "../../lib/trpc/routers/auth";
 import { trpc } from "../../lib/trpc";
-
-type AuthState = RouterOutputs["auth"]["onAuthState"];
📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between b9a6d5b and fccc6c7.

📒 Files selected for processing (6)
  • apps/desktop/src/lib/trpc/routers/auth/index.ts
  • apps/desktop/src/main/lib/auth/auth.ts
  • apps/desktop/src/main/lib/auth/index.ts
  • apps/desktop/src/renderer/contexts/AuthProvider/AuthProvider.tsx
  • apps/desktop/src/renderer/contexts/TRPCProvider/TRPCProvider.tsx
  • apps/desktop/src/renderer/lib/trpc-client.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • apps/desktop/src/main/lib/auth/auth.ts
🧰 Additional context used
📓 Path-based instructions (6)
apps/desktop/**/*.{ts,tsx}

📄 CodeRabbit inference engine (apps/desktop/AGENTS.md)

apps/desktop/**/*.{ts,tsx}: For Electron interprocess communication, ALWAYS use tRPC as defined in src/lib/trpc
Use alias as defined in tsconfig.json when possible
Prefer zustand for state management if it makes sense. Do not use effect unless absolutely necessary.
For tRPC subscriptions with trpc-electron, ALWAYS use the observable pattern from @trpc/server/observable instead of async generators, as the library explicitly checks isObservable(result) and throws an error otherwise

Files:

  • apps/desktop/src/renderer/contexts/TRPCProvider/TRPCProvider.tsx
  • apps/desktop/src/lib/trpc/routers/auth/index.ts
  • apps/desktop/src/renderer/contexts/AuthProvider/AuthProvider.tsx
  • apps/desktop/src/renderer/lib/trpc-client.ts
  • apps/desktop/src/main/lib/auth/index.ts
**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{ts,tsx}: Use object parameters for functions with 2+ parameters instead of positional arguments
Functions with 2+ parameters should accept a single params object with named properties for self-documentation and extensibility
Use prefixed console logging with context pattern: [domain/operation] message
Extract magic numbers and hardcoded values to named constants at module top
Use lookup objects/maps instead of repeated if (type === ...) conditionals
Avoid using any type - maintain type safety in TypeScript code
Never swallow errors silently - at minimum log them with context
Import from concrete files directly when possible - avoid barrel file abuse that creates circular dependencies
Avoid deep nesting (4+ levels) - use early returns, extract functions, and invert conditions
Use named properties in options objects instead of boolean parameters to avoid boolean blindness

Files:

  • apps/desktop/src/renderer/contexts/TRPCProvider/TRPCProvider.tsx
  • apps/desktop/src/lib/trpc/routers/auth/index.ts
  • apps/desktop/src/renderer/contexts/AuthProvider/AuthProvider.tsx
  • apps/desktop/src/renderer/lib/trpc-client.ts
  • apps/desktop/src/main/lib/auth/index.ts
apps/desktop/src/renderer/**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

Never import Node.js modules (fs, path, os, net) in renderer process or shared code - they are externalized for browser compatibility

Files:

  • apps/desktop/src/renderer/contexts/TRPCProvider/TRPCProvider.tsx
  • apps/desktop/src/renderer/contexts/AuthProvider/AuthProvider.tsx
  • apps/desktop/src/renderer/lib/trpc-client.ts
**/*.tsx

📄 CodeRabbit inference engine (AGENTS.md)

One component per file - do not create multi-component files

Files:

  • apps/desktop/src/renderer/contexts/TRPCProvider/TRPCProvider.tsx
  • apps/desktop/src/renderer/contexts/AuthProvider/AuthProvider.tsx
apps/**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

Use Drizzle ORM for all database operations - never use raw SQL

Files:

  • apps/desktop/src/renderer/contexts/TRPCProvider/TRPCProvider.tsx
  • apps/desktop/src/lib/trpc/routers/auth/index.ts
  • apps/desktop/src/renderer/contexts/AuthProvider/AuthProvider.tsx
  • apps/desktop/src/renderer/lib/trpc-client.ts
  • apps/desktop/src/main/lib/auth/index.ts
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (AGENTS.md)

Use Biome for formatting and linting - run at root level with bun run lint:fix or biome check --write

Files:

  • apps/desktop/src/renderer/contexts/TRPCProvider/TRPCProvider.tsx
  • apps/desktop/src/lib/trpc/routers/auth/index.ts
  • apps/desktop/src/renderer/contexts/AuthProvider/AuthProvider.tsx
  • apps/desktop/src/renderer/lib/trpc-client.ts
  • apps/desktop/src/main/lib/auth/index.ts
🧠 Learnings (13)
📓 Common learnings
Learnt from: CR
Repo: superset-sh/superset PR: 0
File: apps/desktop/AGENTS.md:0-0
Timestamp: 2025-12-21T04:39:28.543Z
Learning: Applies to apps/desktop/**/*.{ts,tsx} : For Electron interprocess communication, ALWAYS use tRPC as defined in `src/lib/trpc`
📚 Learning: 2025-12-21T04:39:28.543Z
Learnt from: CR
Repo: superset-sh/superset PR: 0
File: apps/desktop/AGENTS.md:0-0
Timestamp: 2025-12-21T04:39:28.543Z
Learning: Applies to apps/desktop/**/*.{ts,tsx} : For Electron interprocess communication, ALWAYS use tRPC as defined in `src/lib/trpc`

Applied to files:

  • apps/desktop/src/renderer/contexts/TRPCProvider/TRPCProvider.tsx
  • apps/desktop/src/renderer/lib/trpc-client.ts
📚 Learning: 2026-01-02T06:50:28.671Z
Learnt from: CR
Repo: superset-sh/superset PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-02T06:50:28.671Z
Learning: Applies to apps/api/**/*.{ts,tsx} : Use TRPCError with appropriate error codes: NOT_FOUND, UNAUTHORIZED, FORBIDDEN, BAD_REQUEST, INTERNAL_SERVER_ERROR, NOT_IMPLEMENTED

Applied to files:

  • apps/desktop/src/renderer/contexts/TRPCProvider/TRPCProvider.tsx
  • apps/desktop/src/renderer/lib/trpc-client.ts
📚 Learning: 2026-01-02T06:50:28.671Z
Learnt from: CR
Repo: superset-sh/superset PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-02T06:50:28.671Z
Learning: Applies to apps/api/**/*.{ts,tsx} : Extract business logic from tRPC procedures into utility functions when logic exceeds ~50 lines, is used by multiple procedures, or needs independent testing

Applied to files:

  • apps/desktop/src/renderer/contexts/TRPCProvider/TRPCProvider.tsx
  • apps/desktop/src/renderer/lib/trpc-client.ts
📚 Learning: 2025-12-21T04:39:28.543Z
Learnt from: CR
Repo: superset-sh/superset PR: 0
File: apps/desktop/AGENTS.md:0-0
Timestamp: 2025-12-21T04:39:28.543Z
Learning: Applies to apps/desktop/**/*.{ts,tsx} : For tRPC subscriptions with trpc-electron, ALWAYS use the observable pattern from `trpc/server/observable` instead of async generators, as the library explicitly checks `isObservable(result)` and throws an error otherwise

Applied to files:

  • apps/desktop/src/renderer/contexts/TRPCProvider/TRPCProvider.tsx
  • apps/desktop/src/renderer/lib/trpc-client.ts
📚 Learning: 2026-01-02T06:50:28.671Z
Learnt from: CR
Repo: superset-sh/superset PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-02T06:50:28.671Z
Learning: Applies to apps/desktop/src/renderer/**/*.{ts,tsx} : Never import Node.js modules (fs, path, os, net) in renderer process or shared code - they are externalized for browser compatibility

Applied to files:

  • apps/desktop/src/renderer/contexts/TRPCProvider/TRPCProvider.tsx
  • apps/desktop/src/renderer/lib/trpc-client.ts
📚 Learning: 2025-12-21T04:39:28.543Z
Learnt from: CR
Repo: superset-sh/superset PR: 0
File: apps/desktop/AGENTS.md:0-0
Timestamp: 2025-12-21T04:39:28.543Z
Learning: Applies to apps/desktop/**/*.{ts,tsx} : Prefer zustand for state management if it makes sense. Do not use effect unless absolutely necessary.

Applied to files:

  • apps/desktop/src/renderer/contexts/AuthProvider/AuthProvider.tsx
📚 Learning: 2026-01-02T06:50:28.671Z
Learnt from: CR
Repo: superset-sh/superset PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-02T06:50:28.671Z
Learning: Applies to apps/desktop/src/shared/ipc-channels.ts : Define IPC channel types in apps/desktop/src/shared/ipc-channels.ts before implementing handlers

Applied to files:

  • apps/desktop/src/renderer/lib/trpc-client.ts
📚 Learning: 2026-01-02T06:50:28.671Z
Learnt from: CR
Repo: superset-sh/superset PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-02T06:50:28.671Z
Learning: Applies to apps/desktop/src/lib/*.ts : Never import Node.js modules in shared code like electron-router-dom.ts - it runs in both main and renderer processes

Applied to files:

  • apps/desktop/src/renderer/lib/trpc-client.ts
📚 Learning: 2026-01-02T06:50:28.671Z
Learnt from: CR
Repo: superset-sh/superset PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-02T06:50:28.671Z
Learning: Applies to apps/desktop/src/main/lib/*-ipcs.ts : IPC handlers must accept a single object parameter, not positional parameters

Applied to files:

  • apps/desktop/src/renderer/lib/trpc-client.ts
📚 Learning: 2026-01-02T06:50:28.671Z
Learnt from: CR
Repo: superset-sh/superset PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-02T06:50:28.671Z
Learning: Applies to apps/desktop/src/lib/electron-router-dom.ts : Do not import Node.js modules like node:path or dotenv in electron-router-dom.ts and similar shared files - they run in both main and renderer processes

Applied to files:

  • apps/desktop/src/renderer/lib/trpc-client.ts
📚 Learning: 2026-01-02T06:50:28.671Z
Learnt from: CR
Repo: superset-sh/superset PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-02T06:50:28.671Z
Learning: Applies to apps/api/**/*.{ts,tsx} : tRPC procedures and API route handlers should validate and delegate; keep orchestrators thin

Applied to files:

  • apps/desktop/src/renderer/lib/trpc-client.ts
📚 Learning: 2026-01-02T06:50:28.671Z
Learnt from: CR
Repo: superset-sh/superset PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-02T06:50:28.671Z
Learning: Applies to apps/**/proxy.ts : Never use `middleware.ts` in Next.js 16. Use `proxy.ts` instead for request interception.

Applied to files:

  • apps/desktop/src/renderer/lib/trpc-client.ts
🧬 Code graph analysis (4)
apps/desktop/src/renderer/contexts/TRPCProvider/TRPCProvider.tsx (1)
apps/desktop/src/renderer/lib/trpc-client.ts (1)
  • reactClient (20-22)
apps/desktop/src/lib/trpc/routers/auth/index.ts (4)
apps/desktop/src/main/lib/auth/auth.ts (1)
  • AuthSession (50-50)
apps/desktop/src/lib/trpc/index.ts (1)
  • router (47-47)
packages/trpc/src/trpc.ts (1)
  • publicProcedure (31-31)
packages/trpc/src/index.ts (1)
  • publicProcedure (12-12)
apps/desktop/src/renderer/contexts/AuthProvider/AuthProvider.tsx (1)
apps/desktop/src/lib/trpc/routers/auth/index.ts (1)
  • AuthState (8-8)
apps/desktop/src/renderer/lib/trpc-client.ts (1)
apps/desktop/src/lib/trpc/routers/index.ts (1)
  • AppRouter (47-47)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: Cursor Bugbot
  • GitHub Check: Build
🔇 Additional comments (6)
apps/desktop/src/renderer/contexts/TRPCProvider/TRPCProvider.tsx (1)

1-27: LGTM! Clean consolidation to shared tRPC client.

The refactor correctly addresses the IPC ID collision bug by using the shared reactClient instead of creating a local client. The QueryClient remains component-scoped (via useState), which is correct for React Query's cache isolation per app instance.

Based on learnings, this properly uses tRPC as defined in src/lib/trpc for Electron IPC.

apps/desktop/src/renderer/lib/trpc-client.ts (1)

7-29: Excellent fix with clear documentation.

The shared ipcLink approach correctly solves the ID collision issue. The documentation on lines 7-14 is particularly valuable—it explains the root cause and why the sharing constraint exists, which will prevent future regressions.

Both clients (reactClient for React hooks, trpcClient for imperative calls) now share the same underlying IPC link and its ID counter, ensuring responses are routed to the correct operations.

Based on learnings, this properly uses tRPC as defined in src/lib/trpc for Electron IPC.

apps/desktop/src/main/lib/auth/index.ts (1)

1-2: LGTM!

Clean addition of the AuthSession type re-export alongside the existing exports. This properly exposes the corrected type for use in the auth router and other modules.

apps/desktop/src/lib/trpc/routers/auth/index.ts (2)

7-8: LGTM - Well-defined AuthState type.

The explicit AuthState type cleanly combines AuthSession with the token field. This provides a clear contract for subscription consumers and helps prevent the type confusion mentioned in the PR objectives.


12-43: LGTM - Subscription correctly uses observable pattern.

The subscription properly uses observable<AuthState> from @trpc/server/observable, which is required for trpc-electron compatibility. The cleanup function correctly unsubscribes from both event handlers.

apps/desktop/src/renderer/contexts/AuthProvider/AuthProvider.tsx (1)

7-20: LGTM - Clean derivation of token and session from auth state.

The context value correctly extracts token and session from the subscription data with proper null handling. This aligns with the PR's fix for the AuthSession type to correctly extract session data from the API response.

Kitenite and others added 2 commits January 11, 2026 18:16
The previous approach of sharing ipcLink doesn't work because each
tRPC client still creates its own IPCClient instance when the link
factory is called during initialization.

The correct fix is to use a global operation ID counter (sessionIdLink)
that replaces each operation's ID with a unique value from a shared
counter. This ensures operations from both the React client and
proxy client get distinct IDs.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@Kitenite Kitenite changed the title fix(desktop): use single tRPC client to prevent IPC ID collisions fix(desktop): prevent IPC ID collisions with global operation ID counter Jan 12, 2026
🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@Kitenite Kitenite merged commit a77cba2 into main Jan 12, 2026
6 checks passed
@Kitenite Kitenite deleted the fix/desktop-trpc-client-id-collision branch January 12, 2026 03:22
@github-actions
Copy link
Copy Markdown
Contributor

🧹 Preview Cleanup Complete

The following preview resources have been cleaned up:

  • ⚠️ Neon database branch
  • ⚠️ Electric Fly.io app

Thank you for your contribution! 🎉

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants