gocolly/colly · error
ErrRetryBodyUnseekable
ErrRetryBodyUnseekable
Error message
Retry Body Unseekable
What it means
ErrRetryBodyUnseekable is returned by Request.Retry when the request has a non-nil Body that does not implement io.ReadSeeker (request.go:160). Retrying a POST requires rewinding the body to its start; if the body cannot seek, colly cannot safely resend it. Declared at colly.go:252.
Source
Thrown at colly.go:252
ErrNoURLFiltersMatch = errors.New("No URLFilters match")
// ErrRobotsTxtBlocked is the error type for robots.txt errors
ErrRobotsTxtBlocked = errors.New("URL blocked by robots.txt")
// ErrNoCookieJar is the error type for missing cookie jar
ErrNoCookieJar = errors.New("Cookie jar is not available")
// ErrNoPattern is the error type for LimitRules without patterns
ErrNoPattern = errors.New("No pattern defined in LimitRule")
// ErrEmptyProxyURL is the error type for empty Proxy URL list
ErrEmptyProxyURL = errors.New("Proxy URL list is empty")
// ErrAbortedAfterHeaders is the error returned when OnResponseHeaders aborts the transfer.
ErrAbortedAfterHeaders = errors.New("Aborted after receiving response headers")
// ErrAbortedBeforeRequest is the error returned when OnResponseHeaders aborts the transfer.
ErrAbortedBeforeRequest = errors.New("Aborted before Do Request")
// ErrQueueFull is the error returned when the queue is full
ErrQueueFull = errors.New("Queue MaxSize reached")
// ErrMaxRequests is the error returned when exceeding max requests
ErrMaxRequests = errors.New("Max Requests limit reached")
// ErrRetryBodyUnseekable is the error when retry with not seekable body
ErrRetryBodyUnseekable = errors.New("Retry Body Unseekable")
)
var envMap = map[string]func(*Collector, string){
"ALLOWED_DOMAINS": func(c *Collector, val string) {
c.AllowedDomains = strings.Split(val, ",")
},
"CACHE_DIR": func(c *Collector, val string) {
c.CacheDir = val
},
"DETECT_CHARSET": func(c *Collector, val string) {
c.DetectCharset = isYesString(val)
},
"DISABLE_COOKIES": func(c *Collector, _ string) {
c.backend.Client.Jar = nil
},
"DISALLOWED_DOMAINS": func(c *Collector, val string) {
c.DisallowedDomains = strings.Split(val, ",")
},View on GitHub (pinned to 17d1d6ca92)
Solutions
- Build the request body from a seekable type such as bytes.NewReader(payload) or bytes.Buffer
- If the body cannot be seekable, re-issue the request with c.Post instead of calling Retry
- Only call Retry for GET/HEAD requests (nil body), which never hit this error
- Use errors.Is(err, colly.ErrRetryBodyUnseekable) to fall back to manual re-posting
Example fix
// before r.Body = ioutil.NopCloser(strings.NewReader(form)) // not a ReadSeeker // after r.Body = bytes.NewReader([]byte(form)) // implements io.ReadSeeker
Defensive patterns
Strategy: validation
Validate before calling
if r.Body != nil {
if _, ok := r.Body.(io.ReadSeeker); !ok {
// rebuild body as *bytes.Reader before enabling Retry
}
} Type guard
func bodySeekable(r *colly.Request) bool {
if r.Body == nil { return true }
_, ok := r.Body.(io.ReadSeeker)
return ok
} Try / catch
if err := r.Request.Retry(); err != nil {
if errors.Is(err, colly.ErrRetryBodyUnseekable) {
// fall back to re-issuing via c.Post with a fresh bytes.Reader body
return
}
log.Println(err)
} Prevention
- Always construct POST bodies with bytes.NewReader/bytes.Buffer
- Avoid assigning streaming readers (pipes, network streams) as request bodies when retries are planned
- Only call Retry for body-less GET/HEAD requests when the body type is unknown
- Wrap retry logic with an errors.Is check and a manual re-post fallback
When it happens
Trigger: Calling r.Request.Retry() from OnError/OnResponse for a request whose Body was set to a non-seekable reader (e.g. an io.Reader from a pipe or streaming source) instead of bytes.Reader/bytes.Buffer.
Common situations: POST requests with bodies built from os.Stdin, network streams, or multipart readers; retries triggered by 429/5xx handling on such requests.
Related errors
AI-assisted analysis of gocolly/colly@17d1d6ca92 (2026-08-30).
Data as JSON: /api/errors/6f442afdbde21c76.
Report an issue: GitHub.