nektos/act · error

parse %q: %w

Error message

parse %q: %w

What it means

parseContentRange parses the Content-Range header of chunked cache uploads but only supports the 'bytes START-STOP/*' form. After stripping the 'bytes ' prefix and cutting at '/', it strconv.ParseInt's the segment before the '-'. This error means the start-offset portion was not a valid integer — e.g. 'bytes a-5/*', 'bytes -5/*' (empty start), or a completely different header shape.

Source

Thrown at pkg/artifactcache/handler.go:569

		h.logger.Errorf("%v %v: %v", r.Method, r.RequestURI, err)
		data, _ = json.Marshal(map[string]any{
			"error": err.Error(),
		})
	} else {
		data, _ = json.Marshal(v[0])
	}
	w.WriteHeader(code)
	_, _ = w.Write(data)
}

func parseContentRange(s string) (int64, int64, error) {
	// support the format like "bytes 11-22/*" only
	s, _, _ = strings.Cut(strings.TrimPrefix(s, "bytes "), "/")
	s1, s2, _ := strings.Cut(s, "-")

	start, err := strconv.ParseInt(s1, 10, 64)
	if err != nil {
		return 0, 0, fmt.Errorf("parse %q: %w", s, err)
	}
	stop, err := strconv.ParseInt(s2, 10, 64)
	if err != nil {
		return 0, 0, fmt.Errorf("parse %q: %w", s, err)
	}
	return start, stop, nil
}

View on GitHub (pinned to 4f41128141)

Solutions

  1. Pin/upgrade to actions/cache@v3 (or v4) which act's cache server is built against
  2. If calling the API manually, send Content-Range exactly as 'bytes <start>-<stop>/*'
  3. Log the raw header value at the handler to confirm what the client actually sent
  4. Check for HTTP middleware/proxies that rewrite or strip Content-Range

Example fix

// curl upload with correct header
curl -X PATCH "http://localhost:8080/_apis/artifactcache/cache/<id>" \
  -H "Content-Range: bytes 0-1023/*" \
  --data-binary @chunk.bin
Defensive patterns

Strategy: validation

Validate before calling

// validate Content-Range before using it
func validContentRange(s string) bool {
    body := strings.TrimPrefix(s, "bytes ")
    body, _, _ = strings.Cut(body, "/")
    s1, s2, ok := strings.Cut(body, "-")
    if !ok { return false }
    _, e1 := strconv.ParseInt(s1, 10, 64)
    _, e2 := strconv.ParseInt(s2, 10, 64)
    return e1 == nil && e2 == nil
}

Try / catch

start, stop, err := parseContentRange(hdr)
if err != nil {
    http.Error(w, "400 bad Content-Range: "+err.Error(), http.StatusBadRequest)
    return
}

Prevention

When it happens

Trigger: A PATCH request to the local cache server (handler.go:277) whose Content-Range header has a non-numeric or missing start value. Usually produced by a caching action version that formats Content-Range differently than 'bytes N-M/*'.

Common situations: Using actions/cache versions whose upload client sends 'bytes */N' or bare offsets; a custom action or curl-based test PATCH with a hand-written header; proxies/retries mangling the header.

Related errors


AI-assisted analysis of nektos/act@4f41128141 (2026-08-15). Data as JSON: /api/errors/5ce24ca80cde7894. Report an issue: GitHub.