{"record":{"id":"6f442afdbde21c76","repo":"gocolly/colly","slug":"errretrybodyunseekable","errorCode":"ErrRetryBodyUnseekable","errorMessage":"Retry Body Unseekable","messagePattern":"Retry Body Unseekable","errorType":"error_code","errorClass":null,"httpStatus":null,"severity":"error","filePath":"colly.go","lineNumber":252,"sourceCode":"\tErrNoURLFiltersMatch = errors.New(\"No URLFilters match\")\n\t// ErrRobotsTxtBlocked is the error type for robots.txt errors\n\tErrRobotsTxtBlocked = errors.New(\"URL blocked by robots.txt\")\n\t// ErrNoCookieJar is the error type for missing cookie jar\n\tErrNoCookieJar = errors.New(\"Cookie jar is not available\")\n\t// ErrNoPattern is the error type for LimitRules without patterns\n\tErrNoPattern = errors.New(\"No pattern defined in LimitRule\")\n\t// ErrEmptyProxyURL is the error type for empty Proxy URL list\n\tErrEmptyProxyURL = errors.New(\"Proxy URL list is empty\")\n\t// ErrAbortedAfterHeaders is the error returned when OnResponseHeaders aborts the transfer.\n\tErrAbortedAfterHeaders = errors.New(\"Aborted after receiving response headers\")\n\t// ErrAbortedBeforeRequest is the error returned when OnResponseHeaders aborts the transfer.\n\tErrAbortedBeforeRequest = errors.New(\"Aborted before Do Request\")\n\t// ErrQueueFull is the error returned when the queue is full\n\tErrQueueFull = errors.New(\"Queue MaxSize reached\")\n\t// ErrMaxRequests is the error returned when exceeding max requests\n\tErrMaxRequests = errors.New(\"Max Requests limit reached\")\n\t// ErrRetryBodyUnseekable is the error when retry with not seekable body\n\tErrRetryBodyUnseekable = errors.New(\"Retry Body Unseekable\")\n)\n\nvar envMap = map[string]func(*Collector, string){\n\t\"ALLOWED_DOMAINS\": func(c *Collector, val string) {\n\t\tc.AllowedDomains = strings.Split(val, \",\")\n\t},\n\t\"CACHE_DIR\": func(c *Collector, val string) {\n\t\tc.CacheDir = val\n\t},\n\t\"DETECT_CHARSET\": func(c *Collector, val string) {\n\t\tc.DetectCharset = isYesString(val)\n\t},\n\t\"DISABLE_COOKIES\": func(c *Collector, _ string) {\n\t\tc.backend.Client.Jar = nil\n\t},\n\t\"DISALLOWED_DOMAINS\": func(c *Collector, val string) {\n\t\tc.DisallowedDomains = strings.Split(val, \",\")\n\t},","sourceCodeStart":234,"sourceCodeEnd":270,"githubUrl":"https://github.com/gocolly/colly/blob/17d1d6ca92bd32a5651f34256bf7a2855c967f65/colly.go#L234-L270","documentation":"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.","triggerScenarios":"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.","commonSituations":"POST requests with bodies built from os.Stdin, network streams, or multipart readers; retries triggered by 429/5xx handling on such requests.","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"],"exampleFix":"// before\nr.Body = ioutil.NopCloser(strings.NewReader(form)) // not a ReadSeeker\n// after\nr.Body = bytes.NewReader([]byte(form)) // implements io.ReadSeeker","handlingStrategy":"validation","validationCode":"if r.Body != nil {\n    if _, ok := r.Body.(io.ReadSeeker); !ok {\n        // rebuild body as *bytes.Reader before enabling Retry\n    }\n}","typeGuard":"func bodySeekable(r *colly.Request) bool {\n    if r.Body == nil { return true }\n    _, ok := r.Body.(io.ReadSeeker)\n    return ok\n}","tryCatchPattern":"if err := r.Request.Retry(); err != nil {\n    if errors.Is(err, colly.ErrRetryBodyUnseekable) {\n        // fall back to re-issuing via c.Post with a fresh bytes.Reader body\n        return\n    }\n    log.Println(err)\n}","preventionTips":["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"],"tags":["retry","http-body","io","colly"],"backgroundTag":"body-not-seekable","analyzedSha":"17d1d6ca92bd32a5651f34256bf7a2855c967f65","analyzedAt":"2026-08-30T21:32:31.579Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}