Skip to content

fix: treat CookieJar write methods as potentially throwing#3426

Merged
barjin merged 9 commits intov4from
copilot/handle-cookiejar-errors
Feb 23, 2026
Merged

fix: treat CookieJar write methods as potentially throwing#3426
barjin merged 9 commits intov4from
copilot/handle-cookiejar-errors

Conversation

Copy link
Contributor

Copilot AI commented Feb 20, 2026

  • Explore codebase and understand the issue
  • Fix base-http-client.ts: wrap getCookies() and setCookie() calls in try-catch with console.warn
  • Fix session.ts: wrap setCookie() in try-catch with this.log.warning
  • Revert try-catch from getCookies and getCookieString in session.ts
  • Add comment explaining why the cookie is invalid (domain mismatch) in the existing test
  • Add new test for setCookie using a garbled string that causes real tough-cookie parse failure
  • Run existing tests to verify no regressions (25 tests pass)
Original prompt

This section details on the original issue you should resolve

<issue_title>v4: tough-cookie's CookieJars throw on invalid Set-Cookie response contents</issue_title>
<issue_description>As seen in apify/actor-scraper#236, the internal CookieJar can throw on a deformed Set-Cookie response from the server.

This error is now not caught anywhere and will kill the entire script if used, e.g., from a helper function (like discoverValidSitemaps).

We should treat all CookieJar methods as potentially throwing and only print warnings on thrown errors.</issue_description>

<agent_instructions>Make minimal changes to achieve the goal. Use conventional commit specs for the commit messages and the PR title. Do not use excessive comments; only add comments where strictly necessary for code readability.</agent_instructions>

Comments on the Issue (you are @copilot in this section)


🔒 GitHub Advanced Security automatically protects Copilot coding agent pull requests. You can protect all pull requests by enabling Advanced Security for your repositories. Learn more about Advanced Security.

Copilot AI and others added 2 commits February 20, 2026 13:23
Co-authored-by: barjin <61918049+barjin@users.noreply.github.com>
Co-authored-by: barjin <61918049+barjin@users.noreply.github.com>
Copilot AI changed the title [WIP] Fix CookieJar to handle invalid Set-Cookie responses fix: treat all CookieJar methods as potentially throwing Feb 20, 2026
Copilot AI requested a review from barjin February 20, 2026 13:24
@barjin barjin changed the title fix: treat all CookieJar methods as potentially throwing fix: treat all CookieJar methods as potentially throwing Feb 20, 2026
@barjin barjin marked this pull request as ready for review February 20, 2026 15:11
"access": "public"
},
"dependencies": {
"@apify/log": "^2.5.32",
Copy link
Member

Choose a reason for hiding this comment

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

The only part I'm unsure about is adding @apify/log as a @crawlee/http-client dependency... but well, we gotta log somehow.

I'm open to ideas here.

Copy link
Contributor

Choose a reason for hiding this comment

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

#3399 should resolve this problem, shouldn't it?

Copy link
Member

Choose a reason for hiding this comment

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

@crawlee/http-client should have no dependencies on other Crawlee packages (definitely not @crawlee/core), as it's used from @crawlee/utils - importing @crawlee/core would create a circular dependency. It's a bit of a predicament :/

timeout?: number;
} {
const proxyUrl = options?.proxyUrl ?? options?.session?.proxyInfo?.url;
const cookieJar = options?.cookieJar ?? options?.session?.cookieJar ?? new CookieJar();
Copy link
Contributor

Choose a reason for hiding this comment

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

Seems like it still creates cookie jar if not passed 🤔
then on a line 119 and 127 the jar is used in each request: this.applyCookies(currentRequest, cookieJar) or do I miss smth?

Copy link
Contributor

Choose a reason for hiding this comment

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

upd: took a closer look at the implementation, and the solution looks correct to me.

const cookies = this.cookieJar.getCookiesSync(url);
return cookies.map((c) => toughCookieToBrowserPoolCookie(c));
} catch (e) {
this.log.warning('Could not get cookies from cookie jar.', { url, error: (e as Error).message });
Copy link
Contributor

Choose a reason for hiding this comment

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

For an error to be thrown here, the cookie jar would have to accept a malformed cookie string first and then somehow fail at retrieving it. Is there a realistic way this could happen? If not, I'd perhaps re-throw the error in this case.

Copy link
Member

Choose a reason for hiding this comment

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

Yeah, good point, all the errors I could find in the source are parameter-value related, so probably user's fault.

@copilot revert both getCookies and getCookieString hunks.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done in fd55f27. Reverted both getCookies and getCookieString to their original form without try-catch.

try {
return this.cookieJar.getCookieStringSync(url, {});
} catch (e) {
this.log.warning('Could not get cookie string.', { url, error: (e as Error).message });
Copy link
Contributor

Choose a reason for hiding this comment

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

"access": "public"
},
"dependencies": {
"@apify/log": "^2.5.32",
Copy link
Contributor

Choose a reason for hiding this comment

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

#3399 should resolve this problem, shouldn't it?


test('setCookies will log warning (not throw) on invalid cookies', () => {
const url = 'https://www.example.com';
const cookies = [{ name: 'cookie1', value: 'my-cookie', domain: 'abc.different.domain' }];
Copy link
Contributor

Choose a reason for hiding this comment

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

Would be nice to explain why this cookie is invalid (it's for a different domain than the one that sets it). Also, can we test calling setCookie with a malformed raw cookie string?

Copy link
Member

Choose a reason for hiding this comment

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

@copilot work on this, add a comment and a new test with a malformed cookie string.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done in 36d82f7. Added a comment explaining that abc.different.domain doesn't match the request URL (causing tough-cookie to reject it), and added a new test that spies on setCookieSync to simulate a throw and verifies setCookie handles it gracefully without re-throwing.

Co-authored-by: barjin <61918049+barjin@users.noreply.github.com>
Co-authored-by: barjin <61918049+barjin@users.noreply.github.com>
Copilot AI changed the title fix: treat all CookieJar methods as potentially throwing fix: treat CookieJar write methods as potentially throwing Feb 23, 2026
Comment on lines 227 to 229
vitest.spyOn(session.cookieJar, 'setCookieSync').mockImplementation(() => {
throw new Error('malformed cookie');
});
Copy link
Contributor

Choose a reason for hiding this comment

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

@copilot is the mock implementation necessary? If the cookie content is in fact malformed, the actual implementation of setCookieSync should detect it, right? The current test only seems to ensure that the method catches exceptions thrown from setCookieSync.

Copy link
Contributor

Choose a reason for hiding this comment

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

@barjin tell your agent to get cracking please 😁

Copy link
Member

Choose a reason for hiding this comment

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

@copilot chop chop, back to work

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Good call — the mock wasn't needed. A cookie with domain=evil.com set against https://www.example.com causes the real setCookieSync to throw. Updated in b81fca2.

Copy link
Contributor

@janbuchar janbuchar Feb 23, 2026

Choose a reason for hiding this comment

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

But it throws because the cookie is for a different domain. Try to make an actually malformed cookie string. @copilot

Copy link
Member

Choose a reason for hiding this comment

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

@copilot make a test where the cookie string is a garbled mess (not a well-formed cookie string). This shouldn't fail either.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Updated in 636757c — now uses 'garbled!!!@#$%nonsense' which causes the real tough-cookie parser to throw "Cookie failed to parse".

Co-authored-by: barjin <61918049+barjin@users.noreply.github.com>
Co-authored-by: barjin <61918049+barjin@users.noreply.github.com>
@barjin barjin merged commit 7edc67c into v4 Feb 23, 2026
7 checks passed
@barjin barjin deleted the copilot/handle-cookiejar-errors branch February 23, 2026 14:40
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.

4 participants