{"record":{"id":"c00c94c09aca1710","repo":"apify/crawlee","slug":"remote-browser-endpoint-resolved-to-an-empty-strin","errorCode":null,"errorMessage":"Remote browser endpoint resolved to an empty string.","messagePattern":"Remote browser endpoint resolved to an empty string\\.","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/browser-pool/src/remote-browser-pool.ts","lineNumber":78,"sourceCode":"    readonly #onRelease: ((info: { endpoint: string; context?: Record<string, unknown> }) => unknown) | undefined;\n    readonly #log: CrawleeLogger;\n\n    constructor(\n        endpoint: RemoteBrowserEndpoint,\n        onRelease: ((info: { endpoint: string; context?: Record<string, unknown> }) => unknown) | undefined,\n        log: CrawleeLogger,\n    ) {\n        this.#endpoint = endpoint;\n        this.#onRelease = onRelease;\n        this.#log = log;\n    }\n\n    async resolve(options?: { proxyUrl?: string }): Promise<{ url: string; token: number }> {\n        const resolved = typeof this.#endpoint === 'function' ? await this.#endpoint(options) : this.#endpoint;\n\n        let result: ResolvedRemoteEndpoint;\n        if (typeof resolved === 'string') {\n            if (!resolved) throw new Error('Remote browser endpoint resolved to an empty string.');\n            result = { url: resolved };\n        } else if (!resolved?.url) {\n            throw new Error(\"Remote browser endpoint() must return a URL string or an object with a non-empty 'url'.\");\n        } else {\n            result = resolved;\n        }\n\n        const token = this.#nextToken++;\n        this.#sessions.set(token, { url: result.url, context: result.context, released: false });\n        return { url: result.url, token };\n    }\n\n    async release(token: number): Promise<void> {\n        const session = this.#sessions.get(token);\n        // Release at most once per session — guards a close()/teardown race (the `released` flag is set\n        // synchronously before the awaited onRelease, so releaseAll() can't double-fire an in-flight release).\n        if (!session || session.released) return;\n        session.released = true;","sourceCodeStart":60,"sourceCodeEnd":96,"githubUrl":"https://github.com/apify/crawlee/blob/dbe57fb09ca607ad59dcf998f3925ef9ac3bb26c/packages/browser-pool/src/remote-browser-pool.ts#L60-L96","documentation":"RemoteBrowserPool accepts an endpoint either as a string or a function returning a string/object. When the endpoint is a function, it is awaited and the result validated; if it resolves to an empty string the pool cannot connect, so it throws with this message. It guards against silent misconfiguration producing empty endpoint values.","triggerScenarios":"Providing a function endpoint to RemoteBrowserPool (or the pool config) that returns '' — e.g. an env var that is unset, an API returning an empty url field, or template interpolation of a missing value.","commonSituations":"REMOTE_BROWSER_URL environment variable missing/empty in CI or containers; a provisioning endpoint returning an empty body parsed to ''; race where the browser server hasn't published its URL yet.","solutions":["Fix the endpoint function to return a valid ws/http URL; check the source (env var, config, API) for the empty value.","Add a default/fallback or fail fast at startup: throw early if the configured endpoint is empty.","Log the resolved value before returning it from the endpoint function to catch empty resolutions during development."],"exampleFix":"// before\nnew RemoteBrowserPool({ endpoint: async () => process.env.REMOTE_BROWSER_URL ?? '' });\n// after\nnew RemoteBrowserPool({\n  endpoint: async () => {\n    const url = process.env.REMOTE_BROWSER_URL;\n    if (!url) throw new Error('REMOTE_BROWSER_URL is not set');\n    return url;\n  },\n});","handlingStrategy":"validation","validationCode":"const resolved = await endpointFn();\nif (typeof resolved === 'string' && !resolved) {\n  throw new Error('Remote browser endpoint is empty; check env/config before pool init');\n}","typeGuard":"function isNonEmptyString(v: unknown): v is string {\n  return typeof v === 'string' && v.length > 0;\n}","tryCatchPattern":"try {\n  await remotePool.init();\n} catch (err) {\n  if (err instanceof Error && err.message.includes('resolved to an empty string')) {\n    // re-read env/config or retry provisioning, then re-init\n  } throw err;\n}","preventionTips":["Fail fast at startup if the endpoint env var/config is missing or empty.","Have endpoint functions throw on unresolvable values instead of returning ''.","Log resolved endpoints (redacted) in CI to catch empty provisioning early."],"tags":["browser-pool","remote-browser","configuration","empty-endpoint"],"backgroundTag":"missing-endpoint-url","analyzedSha":"dbe57fb09ca607ad59dcf998f3925ef9ac3bb26c","analyzedAt":"2026-08-30T22:22:28.328Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}