Skip to content

Why did my request fail ​

Start with the Error log. A limit rejection names the property, the configured value, the observed value, and the CLI key. See Limits and escape hatches.

Show logs with titanium run -c twp.yaml -v, or set logging.minimumLevel below Error. The default Error level already includes Enforce rejections. Observe lines need Debug.

Status codes ​

CodeUsual causeWhat to change
400Malformed HTTP/1 framing (obs-fold, a header line with no colon)Fix the client. This is not a limit.
413Buffered body exceeded maxBufferedBodyBytesRaise server.limits.maxBufferedBodyBytes, or stop materializing the body and stream it.
431Request header count or header bytes exceeded the header limitsRaise server.limits.maxHeaderCount or server.limits.maxHeaderAggregateBytes.
502Origin headers, or the origin could not be reached (DNS failure, refused, reset, TLS failure)For headers: server.limits.maxDecodedHeaderListBytes or the HTTP/1 header keys. The 502 body names the host and the reason; an unreachable origin is not a proxy setting.
503Admission: too many client connectionsRaise server.pooling.maxConcurrentClientConnections or server.limits.maxConcurrentClients.
504A timeout firedThe log names the server.timeouts.* key and the configured value (connectTimeOutSeconds, responseHeaderTimeoutSeconds, idleReadTimeoutSeconds, idleWriteTimeoutSeconds, requestTimeoutSeconds, clientHeaderTimeoutSeconds). A connect or response timeout before any response byte is answered with 504, not a silent close.

414 and 421 are not limit codes.

Resets and protocol errors ​

SymptomMeaningKey
Connection reset while headers are largeHTTP/2 compressed header block or decoded listserver.limits.maxHttp2CompressedHeaderBlockBytes, server.limits.maxDecodedHeaderListBytes
RST_STREAM / ENHANCE_YOUR_CALMParked HTTP/2 DATA exceeded the per-stream queueserver.limits.maxDeferredOutboundBytesPerStream
HTTP/2 stream wait, then resetFlow-control window was not updated in timeserver.limits.http2WindowUpdateTimeoutSeconds
HTTP/3 ExcessiveLoadA frame declared more than maxHttp3FramePayloadBytesRaise that key (ceiling 64 MiB). DATA with no body hook streams past the cap; headers and hooks still need the whole frame.
HTTP/3 FrameErrorThe frame ended before the declared payloadThe peer closed early. Not a limit.
WebSocket close 1009One frame exceeded maxWebSocketFramePayloadBytesserver.limits.maxWebSocketFramePayloadBytes
WebSocket close 1002Protocol error (bad opcode, fragmented control frame)Fix the peer.
401 / 407 after a few challengesAuth rounds, upstream proxy attempts, or the Windows token sizeserver.limits.maxAuthChallengeRounds, maxUpstreamProxyAuthenticationAttempts, maxWinAuthTokenBytes
Many 103 / 100 then a failureInterim responsesserver.limits.maxInterimResponses

PublicFacing's 60 s idle and 120 s request deadlines close SSE, gRPC streams, and WebSockets. Raise those timeouts or use Balanced when the client is trusted.

Decrypt-bypass learning is a Warning, not an Error: the current request was not failed. The line names server.enableDecryptFailureBypass. Later CONNECTs to that host tunnel without decryption until the TTL expires.

A client that rejects the Titanium certificate (pinned apps, or a client that does not trust the root) is cut off after a few handshake failures per host (ProxyServer.ClientHandshakeRejectThreshold within ProxyServer.ClientHandshakeRejectWindow), and that host is tunnelled without decryption for the learned-bypass TTL. The log shows one Warning naming the host and one throttled Debug line per host. Install the root CA in the client to decrypt that host again.

If several Titanium Root Certificate Authority roots are in the trust store, new roots carry a Subject Key Identifier and their leaves an Authority Key Identifier, so chains pick the right root. In the Inspector, Remove old root CAs… removes every one except the current root.

A CONNECT tunnel is answered with 200 before the origin is dialed, so an unreachable origin shows up in the browser as a closed connection. Set EstablishServerConnectionBeforeResponse in BeforeTunnelConnectRequest to dial first and answer 502/504 instead. The Inspector does this for opaque (non-decrypted) tunnels, and the dialed connection is reused for the tunnel.

If an origin closes the connection before sending its declared Content-Length bytes, the proxy does not rewrite the response as complete. It closes the client connection (a 502 if no response byte was sent yet, or RST_STREAM on HTTP/2 once headers are out), so the browser reports a failed load instead of keeping a short body.

Core, CLI, and website: MIT