{"record":{"id":"3cf5b59d770f2ccc","repo":"decolua/9router","slug":"bfl-no-polling-url-returned","errorCode":null,"errorMessage":"BFL: no polling_url returned","messagePattern":"BFL: no polling_url returned","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"open-sse/handlers/imageProviders/blackForestLabs.js","lineNumber":27,"sourceCode":"  buildUrl: (model) => `${BASE_URL}/${model}`,\n  buildHeaders: (creds) => {\n    const key = creds?.apiKey || creds?.accessToken;\n    return { \"Content-Type\": \"application/json\", \"x-key\": key };\n  },\n  buildBody: (_model, body) => {\n    const req = { prompt: body.prompt };\n    if (body.size) {\n      const [w, h] = body.size.split(\"x\").map(Number);\n      if (w) req.width = w;\n      if (h) req.height = h;\n    }\n    if (body.image) req.image_prompt = body.image;\n    return req;\n  },\n  async parseResponse(response, { headers }) {\n    const data = await response.json();\n    const pollingUrl = data.polling_url;\n    if (!pollingUrl) throw new Error(\"BFL: no polling_url returned\");\n    const deadline = Date.now() + POLL_TIMEOUT_MS;\n    while (Date.now() < deadline) {\n      await sleep(POLL_INTERVAL_MS);\n      const r = await fetch(pollingUrl, { headers: { \"x-key\": headers[\"x-key\"], \"Accept\": \"application/json\" } });\n      if (!r.ok) throw new Error(`BFL status ${r.status}`);\n      const s = await r.json();\n      if (s.status === \"Ready\") return s;\n      if (s.status === \"Error\" || s.status === \"Failed\") throw new Error(s.error || \"BFL generation failed\");\n    }\n    throw new Error(\"BFL polling timeout\");\n  },\n  normalize: (responseBody) => {\n    const sample = responseBody.result?.sample;\n    if (sample) return { created: nowSec(), data: [{ url: sample }] };\n    return { created: nowSec(), data: [] };\n  },\n};\n","sourceCodeStart":9,"sourceCodeEnd":45,"githubUrl":"https://github.com/decolua/9router/blob/90b52e06ffd666b7929554211474d01588f6b1f8/open-sse/handlers/imageProviders/blackForestLabs.js#L9-L45","documentation":"Thrown by the Black Forest Labs (FLUX) adapter's parseResponse (open-sse/handlers/imageProviders/blackForestLabs.js:27) when the submit response JSON contains no polling_url. BFL generation is asynchronous: the initial POST returns { polling_url } that the adapter polls until status is 'Ready'. Without a polling_url there is nothing to poll, so the adapter treats the response as invalid.","triggerScenarios":"parseResponse is called after a submit POST to https://api.bfl.ai/.../{model} and response.json() parses but data.polling_url is undefined — e.g. BFL returned an error envelope ({ error: ..., status: 4xx }) with HTTP 200, or the response shape changed / a proxy or gateway intercepted and returned a non-BFL JSON body (login page JSON, rate-limit notice).","commonSituations":"Invalid or missing x-key so BFL responds with an error object instead of a task; account out of credits; a corporate proxy or the 9router's own error path returned JSON that isn't a BFL task response; BFL API version change renaming polling_url.","solutions":["Check the actual response body (log data in parseResponse) — it usually contains the real BFL error; fix auth first: ensure the x-key header carries a valid BFL API key.","Verify your BFL account has credits/quota at api.bfl.ai.","Ensure no proxy/CDN is rewriting the response (compare with a direct curl POST to the same endpoint with the same headers).","If BFL changed its API shape, update the adapter to read the new task-URL field."],"exampleFix":"// before\nconst data = await response.json();\nconst pollingUrl = data.polling_url;\n// after: surface the real error when polling_url is absent\nconst data = await response.json();\nconst pollingUrl = data.polling_url;\nif (!pollingUrl) throw new Error(`BFL: no polling_url returned: ${JSON.stringify(data)}`);","handlingStrategy":"try-catch","validationCode":"// pre-flight: verify the key authenticates before submitting a task\nconst probe = await fetch(\"https://api.bfl.ai/v1/my_account\", { headers: { \"x-key\": key } });\nif (!probe.ok) throw new Error(`BFL key invalid: ${probe.status}`);","typeGuard":"function isBflTaskResponse(data) {\n  return data != null && typeof data === \"object\" && typeof data.polling_url === \"string\" && data.polling_url.startsWith(\"http\");\n}","tryCatchPattern":"try {\n  const image = await generateImage({ provider: \"black-forest-labs\", prompt });\n} catch (e) {\n  if (/BFL: no polling_url returned/.test(e.message)) {\n    logBflResponseForDebug(); // response JSON holds the real BFL error\n    return fallbackImageProvider(prompt);\n  }\n  if (/BFL status 429/.test(e.message)) return backoffRetry(3);\n  throw e;\n}","preventionTips":["Validate the BFL API key (x-key) at connection-save time, not at generation time.","Monitor BFL credits/quota — exhausted accounts get error envelopes instead of tasks.","Log the submit response body whenever polling_url is missing to see the real cause.","Bypass intermediary proxies when debugging to rule out rewritten responses."],"tags":["image-generation","async-polling","bfl","flux","api-response"],"backgroundTag":"missing-polling-url","analyzedSha":"90b52e06ffd666b7929554211474d01588f6b1f8","analyzedAt":"2026-08-30T21:05:45.952Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}