gocolly/colly · error

http.StatusText(response.StatusCode)

Error message

http.StatusText(response.StatusCode)

What it means

In Collector.handleOnError, when the error is nil but the response status code is >= 300 and ParseHTTPErrorResponse is false, colly synthesizes err = errors.New(http.StatusText(response.StatusCode)) and invokes OnError. The message is just the standard status text (e.g. "Not Found", "Internal Server Error"). This is colly's default way of treating HTTP error statuses as errors.

Source

Thrown at colly.go:1324

				if c.debugger != nil {
					c.debugger.Event(createEvent("xml", resp.Request.ID, c.ID, map[string]string{
						"selector": cc.Query,
						"url":      resp.Request.URL.String(),
					}))
				}
				cc.Function(e)
			})
		}
	}
	return nil
}

func (c *Collector) handleOnError(response *Response, err error, request *Request, ctx *Context) error {
	if err == nil && (c.ParseHTTPErrorResponse || response.StatusCode < 300) {
		return nil
	}
	if err == nil && response.StatusCode >= 300 {
		err = errors.New(http.StatusText(response.StatusCode))
	}
	if response == nil {
		response = &Response{
			Request: request,
			Ctx:     ctx,
		}
	}
	if c.debugger != nil {
		c.debugger.Event(createEvent("error", request.ID, c.ID, map[string]string{
			"url":    request.URL.String(),
			"status": http.StatusText(response.StatusCode),
		}))
	}
	if response.Request == nil {
		response.Request = request
	}
	if response.Ctx == nil {
		response.Ctx = request.Ctx

View on GitHub (pinned to 17d1d6ca92)

Solutions

  1. Check response.StatusCode inside OnError to distinguish status classes
  2. Set c.ParseHTTPErrorResponse = true if you want error-status responses delivered to OnResponse instead
  3. Inspect the target URL for the cause of the 4xx/5xx (auth, robots, bot detection)
  4. Use errors.Is/inspection of the message against http.StatusText codes to branch your handling

Example fix

// before: default collector, error pages go to OnError
c := colly.NewCollector()
// after: receive error responses in OnResponse
c := colly.NewCollector()
c.ParseHTTPErrorResponse = true
Defensive patterns

Strategy: try-catch

Validate before calling

// preflight: decide policy before crawling
c := colly.NewCollector()
c.ParseHTTPErrorResponse = true // receive >=300 responses in OnResponse instead

Type guard

func isHTTPStatusError(err error) bool {
    if err == nil { return false }
    return http.StatusText(http.StatusTextLength(len(err.Error()))) != "" &&
        slices.ContainsFunc(statusTexts(), func(s string) bool { return err.Error() == s })
}

Try / catch

c.OnError(func(r *colly.Response, err error) {
    if r.StatusCode >= 400 {
        log.Printf("HTTP %d (%s): %s", r.StatusCode, err.Error(), r.Request.URL)
        return
    }
    log.Printf("transport error: %v", err)
})

Prevention

When it happens

Trigger: Any c.Visit/request receiving a response with StatusCode >= 300 while ParseHTTPErrorResponse is false (the default), causing OnError with a status-text error.

Common situations: Hitting 404/403/500 pages during crawls; sites returning 3xx redirects the backend did not follow; developers confused why OnError fires with a plain-English message instead of a network error.

Related errors


AI-assisted analysis of gocolly/colly@17d1d6ca92 (2026-08-30). Data as JSON: /api/errors/db899d283dda4350. Report an issue: GitHub.