Skip to content

Commit a923ab0

Browse files
refactor(desktop): name the database a cookie read failed on
`ChromiumCookieReadError` carried only a reason and a cause, so every `readFailed` and keychain refusal logged identically. A user with several Chromium browsers installed had no way to tell which one refused. The database path is now a structural attribute and the message derives from it, matching how `BrowserSession`'s errors carry their partition. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
1 parent c2fae1b commit a923ab0

1 file changed

Lines changed: 38 additions & 6 deletions

File tree

apps/desktop/src/preview/BrowserImport/ChromiumCookies.ts

Lines changed: 38 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -58,12 +58,18 @@ export class ChromiumCookieReadError extends Schema.TaggedErrorClass<ChromiumCoo
5858
"ChromiumCookieReadError",
5959
{
6060
reason: ChromiumCookieReadReason,
61+
/**
62+
* Which database the read was for. Without it every `readFailed` and
63+
* keychain failure logs identically, and a user with several browsers
64+
* installed has no way to tell which one refused.
65+
*/
66+
cookieDatabasePath: Schema.String,
6167
/** Kept for the log; never surfaced to the user. */
6268
cause: Schema.optional(Schema.Defect()),
6369
},
6470
) {
6571
override get message(): string {
66-
return `Could not read Chromium cookies: ${this.reason}.`;
72+
return `Could not read Chromium cookies at ${this.cookieDatabasePath}: ${this.reason}.`;
6773
}
6874
}
6975

@@ -124,6 +130,7 @@ const toUnixSeconds = (webkitSeconds: number): number | undefined => {
124130
const readMacKeychainPassword = Effect.fn("ChromiumCookies.readMacKeychainPassword")(function* (
125131
service: string,
126132
account: string,
133+
cookieDatabasePath: string,
127134
) {
128135
const password = yield* Effect.try({
129136
try: () => new Keyring.Entry(service, account).getPassword(),
@@ -134,12 +141,16 @@ const readMacKeychainPassword = Effect.fn("ChromiumCookies.readMacKeychainPasswo
134141
const missing = /no (matching )?entry|not found/i.test(message);
135142
return new ChromiumCookieReadError({
136143
reason: missing ? "keychainItemMissing" : "needsKeychainApproval",
144+
cookieDatabasePath,
137145
cause,
138146
});
139147
},
140148
});
141149
if (password === null || password === "") {
142-
return yield* new ChromiumCookieReadError({ reason: "keychainItemMissing" });
150+
return yield* new ChromiumCookieReadError({
151+
reason: "keychainItemMissing",
152+
cookieDatabasePath,
153+
});
143154
}
144155
return password;
145156
});
@@ -210,10 +221,17 @@ export const readChromiumCookies = Effect.fn("ChromiumCookies.readChromiumCookie
210221
if (source.platform !== "darwin") {
211222
// Linux (libsecret) and Windows (DPAPI, and App-Bound Encryption on
212223
// current Chrome) each need their own key path; only macOS is implemented.
213-
return yield* new ChromiumCookieReadError({ reason: "unsupportedPlatform" });
224+
return yield* new ChromiumCookieReadError({
225+
reason: "unsupportedPlatform",
226+
cookieDatabasePath: source.cookieDatabasePath,
227+
});
214228
}
215229

216-
const password = yield* readMacKeychainPassword(source.keychainService, source.keychainAccount);
230+
const password = yield* readMacKeychainPassword(
231+
source.keychainService,
232+
source.keychainAccount,
233+
source.cookieDatabasePath,
234+
);
217235
const key = NodeCrypto.pbkdf2Sync(
218236
password,
219237
MAC_KEY_SALT,
@@ -223,7 +241,14 @@ export const readChromiumCookies = Effect.fn("ChromiumCookies.readChromiumCookie
223241
);
224242

225243
const snapshotPath = yield* snapshotCookieDatabase(source.cookieDatabasePath).pipe(
226-
Effect.mapError((cause) => new ChromiumCookieReadError({ reason: "readFailed", cause })),
244+
Effect.mapError(
245+
(cause) =>
246+
new ChromiumCookieReadError({
247+
reason: "readFailed",
248+
cookieDatabasePath: source.cookieDatabasePath,
249+
cause,
250+
}),
251+
),
227252
);
228253

229254
const rows = yield* Effect.gen(function* () {
@@ -237,7 +262,14 @@ export const readChromiumCookies = Effect.fn("ChromiumCookies.readChromiumCookie
237262
return yield* decodeCookieRows(raw);
238263
}).pipe(
239264
Effect.provide(NodeSqliteClient.layer({ filename: snapshotPath, readonly: true })),
240-
Effect.mapError((cause) => new ChromiumCookieReadError({ reason: "readFailed", cause })),
265+
Effect.mapError(
266+
(cause) =>
267+
new ChromiumCookieReadError({
268+
reason: "readFailed",
269+
cookieDatabasePath: source.cookieDatabasePath,
270+
cause,
271+
}),
272+
),
241273
);
242274

243275
const cookies: ChromiumCookie[] = [];

0 commit comments

Comments
 (0)