github/copilot-sdk · error
failed to parse legacy package-lock.json
Error message
failed to parse legacy package-lock.json: %w
What it means
This error wraps a JSON decoding failure of the downloaded legacy package-lock.json body. The response was received with HTTP 200 but its content could not be parsed into the expected lockfile schema (packages map with version fields). The bundler throws this because it cannot extract the @github/copilot version from malformed content.
Solutions
- Print/curl the URL and inspect what the body actually contains.
- Check for proxy or captive-portal interference returning HTML with a 200 status.
- Retry if the download was truncated due to network flakiness.
- Verify the lockfile schema still has packages.node_modules/@github/copilot.version.
- Validate the JSON with jq to confirm it is well-formed before debugging code.
Example fix
// before
if err := json.NewDecoder(resp.Body).Decode(&packageLock); err != nil {
return "", fmt.Errorf("failed to parse legacy package-lock.json: %w", err)
}
// after
body, _ := io.ReadAll(resp.Body)
if len(bytes.TrimSpace(body)) > 0 && body[0] != '{' {
return "", fmt.Errorf("legacy package-lock.json response is not JSON (got %q...)", body[:32])
}
if err := json.Unmarshal(body, &packageLock); err != nil {
return "", fmt.Errorf("failed to parse legacy package-lock.json: %w", err)
} Defensive patterns
Strategy: validation
Validate before calling
body, _ := io.ReadAll(resp.Body)
if !json.Valid(body) {
return errors.New("response body is not valid JSON")
}
if bytes.Contains(bytes.ToLower(body[:min(len(body),256)]), []byte("<html")) {
return errors.New("got HTML instead of JSON (proxy/portal?)")
} Try / catch
v, err := fetchLegacyCLIVersionFromRepo(ref)
var parseErr error
if errors.As(err, &parseErr) {
// refetch with a fresh request; check the raw body first
} Prevention
- Validate the response starts with '{' before decoding.
- Bypass transparent proxies when fetching raw files.
- Retry truncated downloads; use a client with timeouts.
- Verify the lockfile schema hasn't changed upstream.
When it happens
Trigger: json.NewDecoder(resp.Body).Decode fails — the body is empty, HTML (e.g. a proxy or error page returned with 200), truncated, or not valid JSON, while fetching the legacy package-lock.json in fetchLegacyCLIVersionFromRepo.
Common situations: Corporate proxies or captive portals returning an HTML login page with status 200; truncated downloads on flaky networks; GitHub serving an error page; a lockfile format change that no longer matches the expected schema.
Understand the failure class
Background: "failed to unmarshal" / json.Unmarshal errors: why parsing a response into a Go struct fails and how to fix it — this error's family across 23 libraries.
- Parsing and encoding errors: unexpected token, malformed input — why parsers reject input and how to find the real culprit.
Related errors
- failed to parse package.json
- Unexpected trailing content at position
- Unescaped control character at position <pos-1>
- Unterminated string escape at position
- Invalid escape sequence \
AI-assisted analysis of github/copilot-sdk@cd8cf15dc3 (2026-09-09).
Data as JSON: /api/errors/fdedc340de368a96.
Report an issue: GitHub.
Appendix: source
Thrown at go/cmd/bundler/main.go:367
url := fmt.Sprintf(packageLockURLFmt, gitRef)
fmt.Printf("Falling back to %s...\n", url)
resp, err := http.Get(url)
if err != nil {
return "", fmt.Errorf("failed to fetch legacy package-lock.json: %w", err)
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
return "", fmt.Errorf("failed to fetch legacy package-lock.json: %s", resp.Status)
}
var packageLock struct {
Packages map[string]struct {
Version string `json:"version"`
} `json:"packages"`
}
if err := json.NewDecoder(resp.Body).Decode(&packageLock); err != nil {
return "", fmt.Errorf("failed to parse legacy package-lock.json: %w", err)
}
pkg, ok := packageLock.Packages["node_modules/@github/copilot"]
if !ok || pkg.Version == "" {
return "", fmt.Errorf("could not find copilotCliVersion in package.json or @github/copilot in package-lock.json")
}
return pkg.Version, nil
}
// isHex returns true if s contains only hexadecimal characters.
func isHex(s string) bool {
for _, c := range s {
if (c < '0' || c > '9') && (c < 'a' || c > 'f') && (c < 'A' || c > 'F') {
return false
}
}
return true
}
View on GitHub (pinned to cd8cf15dc3)