Connection handling and timeouts
If your integration uses a pooled HTTP client — which includes most server-side HTTP libraries by default — read this page before going live. Getting these settings wrong produces intermittent failures that are difficult to diagnose, because they leave no error response and no HTTP status code.
Idle connection timeout
api.toq.io closes idle HTTP connections after 60 seconds.
This applies to keep-alive connections held open between requests. If your client sends no data on a connection for 60 seconds, the platform closes it.
This is normal and expected behaviour for an HTTP service. It becomes a problem only when a client holds a pooled connection open across a gap longer than 60 seconds and then reuses it without checking whether it is still alive.
The failure it causes
When a client reuses a connection that the server has already closed, the request is written into a dead socket and the client reads end-of-stream instead of a response. Because no HTTP exchange took place, there is no status code and no response body. Depending on your HTTP library you will see:
| Library | Typical Error |
|---|---|
| Apache HttpClient (Java) | NoHttpResponseException: api.toq.io:443 failed to respond |
| OkHttp (Java/Kotlin) | IOException: unexpected end of stream |
| Node.js | ECONNRESET / socket hang up |
| .NET | HttpRequestException with IOException: The response ended prematurely |
| Python (requests) | ConnectionError: Connection aborted, RemoteDisconnected |
| Go | EOF / unexpected EOF |
These are connection-level errors, not API errors. They do not indicate a problem with your request, your credentials, or the platform's availability. They indicate a stale connection in your client's pool.
The failure is intermittent by nature: it only occurs when a request happens to land on a connection that was reaped during an idle period. Integrations with steady traffic may never see it; integrations with bursty traffic and quiet gaps will see it occasionally.
How to configure your client
Three changes. Any one of them prevents the failure; we recommend all three.
-
Validate connections before reuse. Have your client check that a pooled connection is still usable before issuing a request on it, rather than assuming it is.
-
Set a connection time-to-live below 60 seconds. This makes your client retire connections before the platform does, so the race never arises. 30 seconds is a safe default.
-
Retry once on connection-level failures. A retry opens a fresh connection and will normally succeed immediately. This is safe for the errors listed above, because the request never reached the platform — nothing was processed, so nothing can be duplicated.
Settings by library
| Library | Relevant Settings |
|---|---|
| Apache HttpClient 5 (Java) | PoolingHttpClientConnectionManager: setValidateAfterInactivity(Timeout.ofSeconds(5)), setConnectionTimeToLive(...), plus evictIdleConnections(...) on the client builder |
| Spring Cloud OpenFeign | Configure the underlying client as above, and register a Retryer — RetryableException on a connection-level failure is safe to retry |
| OkHttp | ConnectionPool(maxIdle, keepAliveDuration, TimeUnit.SECONDS) with keepAliveDuration under 60; retryOnConnectionFailure(true) |
| Node.js | On your http.Agent / https.Agent, set a free-socket timeout under 60s. The built-in agent does not validate sockets before reuse; agentkeepalive adds freeSocketTimeout for this purpose |
| .NET | SocketsHttpHandler.PooledConnectionIdleTimeout set under 60 seconds; consider PooledConnectionLifetime as well |
| Python (requests / urllib3) | Pooled connections are not validated before reuse. Configure urllib3.Retry with connection retries enabled, and recycle sessions periodically |
| Go | http.Transport.IdleConnTimeout set under 60 seconds |
Setting names and defaults vary by version — check the documentation for the version you are running.
Retry guidance
Retry the connection-level errors listed above once, immediately. If a retry also fails, treat it as a genuine connectivity problem and surface it.
Do not retry 4xx responses — those are application errors and a retry will return the same result. 401 Unauthorized is the exception: refresh your access token and retry once (see Token lifetime and caching).
If you are still seeing these errors
Open a support ticket and include:
- the full stack trace, not just the exception message
- the exact timestamp with timezone, to millisecond precision if available
- the source IP addresses your integration egresses from
- the client_id used for the failing request
The timestamp and source IP are what let us correlate against platform logs. Without them we cannot distinguish a stale connection from a genuine platform error.
