{"record":{"id":"0a330703b7447d8d","repo":"hasura/graphql-engine","slug":"could-not-read-archive-w","errorCode":null,"errorMessage":"could not read archive: %w","messagePattern":"could not read archive: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"cli/plugins/download/downloader.go","lineNumber":46,"sourceCode":"\t\"strings\"\n\n\t\"github.com/hasura/graphql-engine/cli/v2/internal/errors\"\n)\n\n// download gets a file from the internet in memory and writes it content\n// to a Verifier.\nfunc download(url string, verifier Verifier, fetcher Fetcher) (io.ReaderAt, int64, error) {\n\tvar op errors.Op = \"download.download\"\n\n\tbody, err := fetcher.Get(url)\n\tif err != nil {\n\t\treturn nil, 0, errors.E(op, fmt.Errorf(\"failed to obtain plugin archive: %w\", err))\n\t}\n\tdefer body.Close()\n\n\tdata, err := io.ReadAll(io.TeeReader(body, verifier))\n\tif err != nil {\n\t\treturn nil, 0, errors.E(op, fmt.Errorf(\"could not read archive: %w\", err))\n\t}\n\n\terr = verifier.Verify()\n\tif err != nil {\n\t\treturn bytes.NewReader(data), int64(len(data)), errors.E(op, err)\n\t}\n\n\treturn bytes.NewReader(data), int64(len(data)), nil\n}\n\n// extractZIP extracts a zip file into the target directory.\nfunc extractZIP(targetDir, fileName string, read io.ReaderAt, size int64) error {\n\tvar op errors.Op = \"download.extractZIP\"\n\n\tzipReader, err := zip.NewReader(read, size)\n\tif err != nil {\n\t\treturn errors.E(op, err)\n\t}","sourceCodeStart":28,"sourceCodeEnd":64,"githubUrl":"https://github.com/hasura/graphql-engine/blob/724551b9ae87845594ef0408cff0e50eb6c90dc5/cli/plugins/download/downloader.go#L28-L64","documentation":"The archive body was obtained but reading it failed mid-stream: io.ReadAll over an io.TeeReader(body, verifier) returned an error. This means the connection broke, the reader was interrupted, or (most commonly) the Verifier's Write method returned an error while bytes were being tee'd into it.","triggerScenarios":"Calling Get on a plugin archive when: the HTTP body is cut off mid-transfer (reset connection, content-length mismatch), the Verifier's Write returns an error (e.g. hash verifier still writing after Finalize, or invalid state), or the reader is closed concurrently.","commonSituations":"Flaky networks truncating downloads in CI; a custom Verifier implementation whose Write errors; server closing keep-alive connections early; timeouts on the http.Client too short for large plugin archives.","solutions":["Inspect the wrapped cause — if it names the Verifier, fix the Verifier's Write path (it must not error during streaming).","Increase the Fetcher's HTTP client timeout / use a longer overall deadline for large archives.","Retry the download; mid-stream resets are frequently transient.","If persistent, download the archive manually with curl to confirm the server delivers the full content-length."],"exampleFix":"// before\nbody, err := fetcher.Get(url)\ndata, err := io.ReadAll(io.TeeReader(body, verifier)) // mid-stream failure\n\n// after\nclient := &http.Client{Timeout: 5 * time.Minute}\nbody, err := client.Get(url)\ndata, err := io.ReadAll(io.TeeReader(body, verifier))","handlingStrategy":"retry","validationCode":"// Ensure verifier is ready to stream writes before Get\nif v, ok := verifier.(interface{ Reset() }); ok { v.Reset() }","typeGuard":null,"tryCatchPattern":"if _, err := downloader.Get(...); err != nil && strings.Contains(err.Error(), \"could not read archive\") {\n    // transient: retry with fresh verifier, or surface as network flake\n}","preventionTips":["Size client timeouts to the largest expected archive","Make custom Verifier.Write never return errors during streaming","Verify content-length matches bytes read after download"],"tags":["download","io","plugin","network"],"backgroundTag":"download-interrupted","analyzedSha":"724551b9ae87845594ef0408cff0e50eb6c90dc5","analyzedAt":"2026-08-28T07:32:55.105Z","schemaVersion":2},"datasetVersion":"2026-08-28T11:17:15.048Z"}