{"record":{"id":"e0916620572e85a3","repo":"ipfs/kubo","slug":"could-not-read-versions-w","errorCode":null,"errorMessage":"could not read versions: %w","messagePattern":"could not read versions: %w","errorType":"error_code","errorClass":null,"httpStatus":null,"severity":"error","filePath":"repo/fsrepo/migrations/versions.go","lineNumber":60,"sourceCode":"func DistVersions(ctx context.Context, fetcher Fetcher, dist string, sortDesc bool) ([]string, error) {\n\tversionBytes, err := fetcher.Fetch(ctx, path.Join(dist, distVersions))\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tprefix := \"v\"\n\tvar vers []semver.Version\n\n\tscan := bufio.NewScanner(bytes.NewReader(versionBytes))\n\tfor scan.Scan() {\n\t\tver, err := semver.Make(strings.TrimLeft(scan.Text(), prefix))\n\t\tif err != nil {\n\t\t\tcontinue\n\t\t}\n\t\tvers = append(vers, ver)\n\t}\n\tif scan.Err() != nil {\n\t\treturn nil, fmt.Errorf(\"could not read versions: %w\", scan.Err())\n\t}\n\n\tif sortDesc {\n\t\tsort.Sort(sort.Reverse(semver.Versions(vers)))\n\t} else {\n\t\tsort.Sort(semver.Versions(vers))\n\t}\n\n\tout := make([]string, len(vers))\n\tfor i := range vers {\n\t\tout[i] = prefix + vers[i].String()\n\t}\n\n\treturn out, nil\n}\n","sourceCodeStart":42,"sourceCodeEnd":76,"githubUrl":"https://github.com/ipfs/kubo/blob/329838acdfafae224582930457efe80aa217afc0/repo/fsrepo/migrations/versions.go#L42-L76","documentation":"DistVersions reads the version list for a distribution via a bufio.Scanner and parses each line with semver. If the scanner encounters an I/O or read error mid-scan (scan.Err() != nil), the versions list is incomplete and the read failure is wrapped as \"could not read versions\". Note it reflects scanner/read failures, not individual unparseable lines (those are skipped with continue).","triggerScenarios":"Calling DistVersions (directly or via LatestDistVersion / fetchBinary flows) when the underlying reader breaks mid-read: network connection reset while streaming the version file, TLS failure, or an aborted response body from the dist server.","commonSituations":"Flaky or proxied network connections dropping mid-download, dist server timeouts, firewall/VPN interference, or transient DNS/TLS issues when reaching the distribution endpoint.","solutions":["Retry the request — the wrapped scan.Err() is usually a transient network failure.","Check connectivity to the distribution endpoint (curl the versions URL) to rule out proxy/firewall blocking.","Inspect the wrapped %w error for the root cause (connection reset, TLS handshake, timeout) and address it.","Configure or wrap the fetcher with sane timeouts/retries before calling DistVersions."],"exampleFix":null,"handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":"var vers []string\nvar err error\nfor i := 0; i < 3; i++ {\n\tvers, err = migrations.DistVersions(ctx, fetcher, dist, false)\n\tif err == nil || !isTransientReadErr(err) {\n\t\tbreak\n\t}\n\tselect {\n\tcase <-time.After(time.Duration(i+1) * time.Second):\n\tcase <-ctx.Done():\n\t\treturn ctx.Err()\n\t}\n}\nif err != nil {\n\treturn fmt.Errorf(\"could not read versions: %w\", err)\n}","preventionTips":["Wrap fetchers with timeouts and bounded retries for transient network faults.","Check connectivity/proxy settings when failures repeat.","Use a context with a deadline so retries abort cleanly."],"tags":["network","io","versioning"],"backgroundTag":"network-read-failure","analyzedSha":"329838acdfafae224582930457efe80aa217afc0","analyzedAt":"2026-09-03T18:30:52.135Z","contentChangedAt":"2026-09-03T18:30:52.135Z","schemaVersion":2},"datasetVersion":"2026-09-11T00:17:11.886Z"}