{"record":{"id":"c131ea3948429a53","repo":"bytedance/deer-flow","slug":"url-is-required","errorCode":null,"errorMessage":"URL is required","messagePattern":"URL is required","errorType":"http","errorClass":"HTTPException","httpStatus":400,"severity":"warning","filePath":"backend/app/gateway/routers/browser.py","lineNumber":97,"sourceCode":")\n@require_permission(\"threads\", \"write\", owner_check=True, require_existing=True)\nasync def navigate_browser(thread_id: ThreadId, body: BrowserNavigateRequest, request: Request) -> BrowserNavigateResponse:\n    user_id = str(request.state.auth.user.id)\n    thread_store = getattr(request.app.state, \"thread_store\", None)\n    if thread_store is None or not await _browser_thread_owned_by(thread_store, thread_id, user_id):\n        raise HTTPException(status_code=404, detail=f\"Thread {thread_id} not found\")\n\n    if not _browser_tools_enabled():\n        raise HTTPException(status_code=404, detail=\"Browser automation is not enabled\")\n\n    try:\n        from deerflow.community.browser_automation import navigate_and_capture, redact_browser_url\n    except ImportError as exc:  # Playwright is an optional dependency.\n        raise HTTPException(status_code=501, detail=\"Browser automation is not available\") from exc\n\n    url = body.url.strip()\n    if not url:\n        raise HTTPException(status_code=400, detail=\"URL is required\")\n\n    outputs_path = get_paths().sandbox_outputs_dir(thread_id, user_id=get_effective_user_id())\n    try:\n        result = await navigate_and_capture(thread_id=thread_id, url=url, outputs_path=outputs_path)\n    except ValueError as exc:\n        # SSRF / URL validation failure.\n        raise HTTPException(status_code=400, detail=str(exc)) from exc\n    except Exception as exc:\n        logger.error(\n            \"Browser navigate failed: thread_id=%s url=%s err_type=%s\",\n            thread_id,\n            redact_browser_url(url),\n            type(exc).__name__,\n        )\n        raise HTTPException(status_code=502, detail=\"Browser navigation failed\") from exc\n\n    return BrowserNavigateResponse(**result)\n","sourceCodeStart":79,"sourceCodeEnd":115,"githubUrl":"https://github.com/bytedance/deer-flow/blob/1dd6ba1acb03700589994b0366c5d1c7d05e2eff/backend/app/gateway/routers/browser.py#L79-L115","documentation":"400 Bad Request from the browser navigate route (browser.py:97): the request body's url field is empty after stripping whitespace. BrowserNavigateRequest.url is required by the schema, but an all-whitespace string ('   ') passes schema presence checks and only fails this explicit trimmed-empty check in the handler.","triggerScenarios":"POST /api/threads/{id}/browser/navigate with body {\"url\": \"\"} or {\"url\": \"   \"} — an empty string is rejected by Pydantic as 422, but whitespace-only reaches the handler and yields this 400.","commonSituations":"Frontend sending an unvalidated input value; automation script passing an unset variable (url=\"\" after a failed shell interpolation).","solutions":["Client-side validate a non-blank URL before issuing the request","Populate the url field with a complete absolute URL (scheme + host), since downstream navigate_and_capture performs SSRF validation expecting a real URL"],"exampleFix":"// before\nawait fetch(`/api/threads/${id}/browser/navigate`, {\n  method: \"POST\",\n  body: JSON.stringify({ url: this.urlInput }), // may be \"\"\n});\n\n// after\nconst url = this.urlInput.trim();\nif (!url) return;\nawait fetch(`/api/threads/${id}/browser/navigate`, {\n  method: \"POST\",\n  body: JSON.stringify({ url }),\n});","handlingStrategy":"validation","validationCode":"const url = rawUrl.trim();\nif (!url) { setFormError(\"URL is required\"); return; }\nawait postNavigate(threadId, url);","typeGuard":"const isNonBlankUrl = (u: unknown): u is string => typeof u === \"string\" && u.trim().length > 0;","tryCatchPattern":"try { await navigateBrowser(threadId, url) } catch (e) { if (e.status === 400 && e.detail === \"URL is required\") setFormError(\"Enter a URL\"); else throw e; }","preventionTips":["Trim and require non-empty input in the UI before enabling the submit action","Send complete absolute URLs (scheme+host) so downstream SSRF validation also passes","Unit-test the form with whitespace-only input"],"tags":["browser","validation","http-400"],"backgroundTag":null,"analyzedSha":"1dd6ba1acb03700589994b0366c5d1c7d05e2eff","analyzedAt":"2026-08-14T21:20:34.804Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}