txthinking/brook · error
Expired request
Error message
Expired request
What it means
The stream server's handshake embeds a 4-byte big-endian Unix timestamp in the authenticated request; NewStreamServer rejects the request if the timestamp is more than 60 seconds in the past. This is a replay-protection window: old captured handshakes cannot be reused. A rejected handshake leaves the connection to be closed via WaitReadErr.
Source
Thrown at streamserver.go:90
s.ca, err = cipher.NewGCM(cb)
if err != nil {
x.BP12.Put(s.cn)
return nil, err
}
s.RB = x.BP2048.Get().([]byte)
l, err := s.Read()
if err != nil {
x.BP12.Put(s.cn)
x.BP2048.Put(s.RB)
return nil, err
}
i := int64(binary.BigEndian.Uint32(s.RB[2+16 : 2+16+4]))
if time.Now().Unix()-i > 60 {
x.BP12.Put(s.cn)
x.BP2048.Put(s.RB)
WaitReadErr(s.Client)
return nil, errors.New("Expired request")
}
if i%2 == 0 {
s.network = "tcp"
}
if i%2 == 1 {
s.network = "udp"
s.Timeout = udptimeout
}
s.sn = x.BP12.Get().([]byte)
if _, err := io.ReadFull(rand.Reader, s.sn); err != nil {
x.BP12.Put(s.cn)
x.BP2048.Put(s.RB)
x.BP12.Put(s.sn)
return nil, err
}
sk := x.BP32.Get().([]byte)
if _, err := io.ReadFull(hkdf.New(sha256.New, password, s.sn, ServerHKDFInfo), sk); err != nil {View on GitHub (pinned to 5cd13ef3b1)
Solutions
- Sync the client clock with NTP and retry - the timestamp must be within 60s of the server clock
- Build the handshake immediately before sending; never cache a prepared request for reuse
- Check for replayed connections (e.g. a proxy re-sending a captured handshake) and generate a fresh one
- If clock skew is unavoidable in the environment, increase the 60s window in the server code
Example fix
// before: handshake built once at startup and reused var hs = buildHandshake() // timestamp frozen conn.Write(hs) // after: rebuild per connection so the timestamp is fresh conn.Write(buildHandshake()) // timestamp = time.Now().Unix()
Defensive patterns
Strategy: retry
Validate before calling
func tsFresh(b []byte) bool {
i := int64(binary.BigEndian.Uint32(b[2+16 : 2+16+4]))
return time.Now().Unix()-i <= 60
} Try / catch
for attempt := 0; attempt < 2; attempt++ {
c, err := NewStreamClient(...) // rebuild handshake each attempt
if err == nil { break }
ntp.Sync() // clock may have drifted
} Prevention
- Run NTP on clients
- Build the handshake immediately before each connection
- Never cache or replay prepared handshakes
When it happens
Trigger: A client embeds a stale timestamp in its handshake (clock skew > 60s between client and server, or a replayed/captured handshake), so time.Now().Unix()-i > 60 on the server.
Common situations: Client machine clock drifting (VM suspend, RTC drift, wrong timezone/NTP disabled); replaying a recorded handshake; queuing a pre-built handshake for more than a minute before sending.
Understand the failure class
Background: Request timed out: what client-side request timeouts mean across libraries (Request timed out, TIMED_OUT, APITimeoutError) — this error's family across 39 libraries.
Related errors
AI-assisted analysis of txthinking/brook@5cd13ef3b1 (2026-09-06).
Data as JSON: /api/errors/82538fb6d3e0da83.
Report an issue: GitHub.