gin-gonic/gin · critical
Cannot redirect with status code %d
Error message
Cannot redirect with status code %d
What it means
Redirect.Render (render/redirect.go:22) panics when the status code is outside the HTTP redirect range [300, 308] and is not 201. http.Redirect only accepts 3xx codes (plus 201 Created with a Location), so any other code (200, 404, 500, etc.) is a programmer error and Gin aborts.
Source
Thrown at render/redirect.go:22
package render
import (
"fmt"
"net/http"
)
// Redirect contains the http request reference and redirects status code and location.
type Redirect struct {
Code int
Request *http.Request
Location string
}
// Render (Redirect) redirects the http request to new location and writes redirect response.
func (r Redirect) Render(w http.ResponseWriter) error {
if (r.Code < http.StatusMultipleChoices || r.Code > http.StatusPermanentRedirect) && r.Code != http.StatusCreated {
panic(fmt.Sprintf("Cannot redirect with status code %d", r.Code))
}
http.Redirect(w, r.Request, r.Location, r.Code)
return nil
}
// WriteContentType (Redirect) don't write any ContentType.
func (r Redirect) WriteContentType(http.ResponseWriter) {}
View on GitHub (pinned to 34dac209ff)
Solutions
- Use a valid redirect status: 301 (MovedPermanently), 302 (Found), 303 (SeeOther), 307 (TemporaryRedirect), 308 (PermanentRedirect), or 201 (Created) with a Location header.
- Use the net/http constants (http.StatusMovedPermanently, http.StatusFound) rather than raw ints.
- If you don't want a redirect, use c.JSON / c.String instead of c.Redirect.
Example fix
// before c.Redirect(http.StatusOK, "/new") // after c.Redirect(http.StatusFound, "/new")
Defensive patterns
Strategy: validation
Validate before calling
func isValidRedirectCode(code int) bool {
return (code >= 300 && code <= 308) || code == http.StatusCreated
}
if !isValidRedirectCode(code) {
return fmt.Errorf("invalid redirect status %d", code)
}
c.Redirect(code, url) Prevention
- Use http.StatusMovedPermanently / http.StatusFound / http.StatusSeeOther / http.StatusTemporaryRedirect / http.StatusPermanentRedirect.
- Reserve 201 only when you really mean Created with a Location header.
- Don't reach for c.Redirect for non-redirect responses — use c.JSON/c.String.
When it happens
Trigger: Calling c.Redirect(http.StatusMovedPermanently, ...) — wait that's valid; calling c.Redirect(http.StatusOK, url) (200 is not a redirect code); c.Redirect(http.StatusFound, ...) is valid but c.Redirect(200, url) panics; passing a custom int that is not 3xx and not 201.
Common situations: Using http.StatusOK or http.StatusBadRequest as the redirect status by mistake; using an enum/constant that resolves to a non-3xx value; copying c.JSON(status, ...) patterns into c.Redirect.
Related errors
- the HTML debug render was created without files or glob patt
- key %v does not exist
- gin mode unknown: ${value} (available mode: debug release te
- http method ${httpMethod} is not valid
- URL parameters can not be used when serving a static file
AI-assisted analysis of gin-gonic/gin@34dac209ff (2026-08-04).
Data as JSON: /data/errors/3fff7a0328aa5af9.json.
Report an issue: GitHub.