Custom Tool HTTP Timeout Configuration#
Custom and API-based tools in Dify use two independent layers of timeout configuration: one specific to the ApiTool execution class, and one at the shared SSRF proxy transport layer. Neither is configurable per-tool from the UI β both are set via environment variables at deployment time.
Layer 1 β ApiTool-Level Timeout (API_TOOL_DEFAULT_*)#
The ApiTool class in api/core/tools/custom_tool/tool.py is the runtime that executes all custom (API-based) tool calls defined via OpenAPI schemas. At module load, it reads two environment variables to construct a connect/read timeout tuple :
| Environment Variable | Default | Controls |
|---|---|---|
API_TOOL_DEFAULT_CONNECT_TIMEOUT | 10 s | TCP connection establishment |
API_TOOL_DEFAULT_READ_TIMEOUT | 60 s | Waiting for the first response byte |
This tuple is passed directly as the timeout argument on every call to ssrf_proxy.* inside do_http_request() . Because the value is baked in at import time using os.getenv(), the process must be restarted for changes to take effect.
Note: These values differ intentionally from the SSRF layer defaults (5 s each). The
ApiToollayer provides the effective timeout for tool calls; the SSRF proxy only applies its own defaults when notimeoutkwarg is present, which is never the case forApiToolinvocations.
Layer 2 β SSRF Proxy Fallback Timeouts (SSRF_DEFAULT_*)#
make_request() in api/core/helper/ssrf_proxy.py is the shared transport used by the ApiTool and many other Dify subsystems. When no timeout kwarg is passed by the caller, it constructs an httpx.Timeout from four dify_config values :
| Environment Variable | Default | Dimension |
|---|---|---|
SSRF_DEFAULT_TIME_OUT | 5 s | Overall request timeout |
SSRF_DEFAULT_CONNECT_TIME_OUT | 5 s | TCP connect |
SSRF_DEFAULT_READ_TIME_OUT | 5 s | Socket read |
SSRF_DEFAULT_WRITE_TIME_OUT | 5 s | Socket write |
These are Pydantic PositiveFloat fields on HttpConfig in api/configs/feature/__init__.py. They apply as fallback defaults for any subsystem that calls ssrf_proxy.* without specifying timeout β but not for ApiTool, which always passes API_TOOL_DEFAULT_TIMEOUT explicitly.
How the Two Layers Interact#
Custom Tool invocation
β
βΌ
ApiTool.do_http_request()
timeout = (API_TOOL_DEFAULT_CONNECT_TIMEOUT, API_TOOL_DEFAULT_READ_TIMEOUT)
β
βΌ
ssrf_proxy.make_request(timeout=...)
β³ "timeout" kwarg IS present β SSRF_DEFAULT_* values are SKIPPED
β
βΌ
httpx.Client.send(request)
Because ApiTool always supplies an explicit timeout, the SSRF_DEFAULT_* variables have no effect on custom tool calls .
Key Source Files#
| File | Purpose |
|---|---|
api/core/tools/custom_tool/tool.py | ApiTool class; defines API_TOOL_DEFAULT_TIMEOUT at L19β22; passes it at L303 |
api/core/helper/ssrf_proxy.py | SSRF transport; fallback timeout logic at L194β200 |
api/configs/feature/__init__.py | Pydantic config for all SSRF_DEFAULT_* fields (L688β703) |