From 7f93b819816f69e0825014300492b9a9de9ae48b Mon Sep 17 00:00:00 2001 From: nikitachapovskii-dev Date: Mon, 10 Aug 2026 13:08:06 +0200 Subject: [PATCH 1/3] fix: gracefully abort the run when Apify Proxy is unusable --- src/input.ts | 16 ++++++++++++++-- src/utils.ts | 19 ++++++++++++++++++- 2 files changed, 32 insertions(+), 3 deletions(-) diff --git a/src/input.ts b/src/input.ts index cb7597b..57eece7 100644 --- a/src/input.ts +++ b/src/input.ts @@ -19,6 +19,7 @@ import type { SERPProxyGroup, UrlToMarkdownInput, } from './types.js'; +import { abortRun } from './utils.js'; /** * Processes the input and returns an array of crawler settings. This is ideal for startup of STANDBY mode @@ -27,7 +28,7 @@ import type { export async function processStandbyInput(originalInput: Partial) { const { input, searchCrawlerOptions, contentScraperSettings } = await processInputInternal(originalInput, true); - const proxy = await Actor.createProxyConfiguration(input.proxyConfiguration); + const proxy = await createContentProxyConfiguration(input.proxyConfiguration); const contentCrawlerOptions: ContentCrawlerOptions[] = [ createPlaywrightCrawlerOptions(input, proxy), createCheerioCrawlerOptions(input, proxy), @@ -42,7 +43,7 @@ export async function processStandbyInput(originalInput: Partial) { export async function processInput(originalInput: Partial) { const { input, searchCrawlerOptions, contentScraperSettings } = await processInputInternal(originalInput); - const proxy = await Actor.createProxyConfiguration(input.proxyConfiguration); + const proxy = await createContentProxyConfiguration(input.proxyConfiguration); const contentCrawlerOptions: ContentCrawlerOptions = input.scrapingTool === 'raw-http' ? createCheerioCrawlerOptions(input, proxy, false) : createPlaywrightCrawlerOptions(input, proxy, false); @@ -210,6 +211,17 @@ async function processUrlToMarkdownInput(input: Partial): Pr return validatedInput; } +async function createContentProxyConfiguration(proxyConfiguration: ProxyConfigurationOptions) { + // A malformed configuration is a user input error, so validate it before checking the proxy access. + await Actor.createProxyConfiguration({ ...proxyConfiguration, checkAccess: false }); + + try { + return await Actor.createProxyConfiguration(proxyConfiguration); + } catch (e) { + return abortRun(`Cannot use Apify Proxy for scraping the target pages: ${(e as Error).message}`); + } +} + function createPlaywrightCrawlerOptions( input: Input, proxy: ProxyConfiguration | undefined, diff --git a/src/utils.ts b/src/utils.ts index 19e2892..39ba29f 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -3,7 +3,7 @@ import { parse } from 'node:querystring'; import { Actor } from 'apify'; import type { ProxyConfiguration, RequestOptions } from 'crawlee'; -import { log } from 'crawlee'; +import { log, sleep } from 'crawlee'; import ragWebBrowserInputSchema from '../actors/apify_rag-web-browser/.actor/input_schema.json' with { type: 'json' }; import urlToMarkdownInputSchema from '../actors/apify_url-to-markdown/.actor/input_schema.json' with { type: 'json' }; @@ -47,6 +47,23 @@ export function isActorStandby(): boolean { return Actor.getEnv().metaOrigin === 'STANDBY'; } +/** + * Aborts the run with a terminal status message, instead of crashing or failing it. + */ +export async function abortRun(statusMessage: string): Promise { + log.error(statusMessage); + + if (!Actor.isAtHome()) { + process.exit(1); + } + + await Actor.abort(Actor.getEnv().actorRunId!, { statusMessage, gracefully: true }); + // Wait for the `aborting` event, on which the SDK shuts the Actor down. + while (true) { + await sleep(1000); + } +} + /** * Extracts the calling end-user's authorization from the x-apify-user-authorization header. * Node lower-cases header names, and the header could theoretically arrive as a string array. From c231694d8de02cd098cd8d6f29004287a5bc853d Mon Sep 17 00:00:00 2001 From: nikitachapovskii-dev Date: Mon, 10 Aug 2026 13:22:20 +0200 Subject: [PATCH 2/3] chore: exit the Actor instead of waiting to be killed --- src/utils.ts | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/utils.ts b/src/utils.ts index 39ba29f..77f7107 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -3,7 +3,7 @@ import { parse } from 'node:querystring'; import { Actor } from 'apify'; import type { ProxyConfiguration, RequestOptions } from 'crawlee'; -import { log, sleep } from 'crawlee'; +import { log } from 'crawlee'; import ragWebBrowserInputSchema from '../actors/apify_rag-web-browser/.actor/input_schema.json' with { type: 'json' }; import urlToMarkdownInputSchema from '../actors/apify_url-to-markdown/.actor/input_schema.json' with { type: 'json' }; @@ -57,11 +57,10 @@ export async function abortRun(statusMessage: string): Promise { process.exit(1); } + // Aborting gracefully buys the 30 seconds that the teardown needs before the container is stopped. await Actor.abort(Actor.getEnv().actorRunId!, { statusMessage, gracefully: true }); - // Wait for the `aborting` event, on which the SDK shuts the Actor down. - while (true) { - await sleep(1000); - } + await Actor.exit({ exit: false }); + process.exit(0); } /** From a17758c1ba0728b83ced6dec56728d491789618d Mon Sep 17 00:00:00 2001 From: nikitachapovskii-dev Date: Mon, 10 Aug 2026 14:16:55 +0200 Subject: [PATCH 3/3] chore: remove validation of proxy configuration --- src/input.ts | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/input.ts b/src/input.ts index 57eece7..f499ab5 100644 --- a/src/input.ts +++ b/src/input.ts @@ -212,9 +212,6 @@ async function processUrlToMarkdownInput(input: Partial): Pr } async function createContentProxyConfiguration(proxyConfiguration: ProxyConfigurationOptions) { - // A malformed configuration is a user input error, so validate it before checking the proxy access. - await Actor.createProxyConfiguration({ ...proxyConfiguration, checkAccess: false }); - try { return await Actor.createProxyConfiguration(proxyConfiguration); } catch (e) {