{"record":{"id":"fe84def87cc262dc","repo":"FlowiseAI/Flowise","slug":"error-message","errorCode":null,"errorMessage":"${error.message}","messagePattern":"\\$\\{error\\.message\\}","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/components/nodes/documentloaders/FireCrawl/FireCrawl.ts","lineNumber":204,"sourceCode":"\n        try {\n            const parameters = {\n                ...validParams,\n                integration: 'flowise'\n            }\n            const response: AxiosResponse = await this.postRequest(this.apiUrl + '/v1/scrape', parameters, headers)\n            if (response.status === 200) {\n                const responseData = response.data\n                if (responseData.success) {\n                    return responseData\n                } else {\n                    throw new Error(`Failed to scrape URL. Error: ${responseData.error}`)\n                }\n            } else {\n                this.handleError(response, 'scrape URL')\n            }\n        } catch (error: any) {\n            throw new Error(error.message)\n        }\n        return { success: false, error: 'Internal server error.' }\n    }\n\n    async crawlUrl(\n        url: string,\n        params: Params | null = null,\n        waitUntilDone: boolean = true,\n        pollInterval: number = 2,\n        idempotencyKey?: string\n    ): Promise<CrawlResponse | CrawlStatusResponse> {\n        const headers = this.prepareHeaders(idempotencyKey)\n\n        // Create a clean payload with only valid parameters\n        const validParams: any = {\n            url\n        }\n","sourceCodeStart":186,"sourceCodeEnd":222,"githubUrl":"https://github.com/FlowiseAI/Flowise/blob/abe4a8601a058047b350c260676826e21dd14101/packages/components/nodes/documentloaders/FireCrawl/FireCrawl.ts#L186-L222","documentation":"Catch-all around scrapeUrl. Anything thrown inside the try (including the [115] success:false throw, handleError throws, and network errors from postRequest -> secureAxiosRequest) is re-wrapped as new Error(error.message). The error type and stack are lost; only the message string survives. This obliterates the distinction between the API-level error [115] and a transport error.","triggerScenarios":"Underlying error is an AxiosError (network/TLS/timeout), an SSRF deny-list rejection, a non-200 status handled by handleError, or the success:false throw from [115].","commonSituations":"User sees a generic message and cannot tell whether the API rejected the request or the network failed; the re-wrap hides axios.isAxiosError so downstream handlers cannot branch on status codes.","solutions":["Read the message: 'Failed to scrape URL. Error:' indicates an API-level failure (see 115); 'Failed to scrape URL. Status code:' indicates a non-200 handled by handleError; a connection error message indicates transport.","Reproduce with curl -H \"Authorization: Bearer $KEY\" against https://api.firecrawl.dev/v1/scrape to isolate FireCrawl-side vs client-side.","Fix the library code to rethrow the original error rather than wrapping: } catch (error) { throw error; } - or attach it via { cause: error }."],"exampleFix":"// before\n} catch (error: any) {\n  throw new Error(error.message)\n}\n// after - preserve type and chain the cause\n} catch (error: any) {\n  if (error instanceof Error) throw error\n  throw new Error(String(error?.message ?? error))\n}","handlingStrategy":"try-catch","validationCode":null,"typeGuard":"function isFirecrawlFailureMessage(msg: string): boolean {\n  return /Failed to scrape URL\\. Error:/.test(msg)\n}\n// detect at call site whether the wrapped error came from [115] vs transport","tryCatchPattern":"try {\n  await app.scrapeUrl(url, params)\n} catch (error) {\n  const msg = error instanceof Error ? error.message : String(error)\n  if (/Failed to scrape URL\\. Error:/.test(msg)) {\n    // FireCrawl-side failure - do not retry the same params unchanged\n  } else if (/ENOTFOUND|ETIMEDOUT|ECONNRESET|Too many redirects/.test(msg)) {\n    // transport - safe to retry with backoff\n    await new Promise((r) => setTimeout(r, 1000))\n    return app.scrapeUrl(url, params)\n  }\n  throw error\n}","preventionTips":["Log the full error (including any axios response) at the call site because the library flattens it.","Distinguish API-level vs transport errors via the message pattern before retrying.","Patch the library to rethrow the original error or attach it via { cause }."],"tags":["error-handling","firecrawl","scraping"],"backgroundTag":null,"analyzedSha":"abe4a8601a058047b350c260676826e21dd14101","analyzedAt":"2026-08-12T16:04:40.823Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}