{"record":{"id":"ab8e5732a5f240da","repo":"router-for-me/CLIProxyAPI","slug":"artifact-checksum-mismatch","errorCode":null,"errorMessage":"artifact checksum mismatch","messagePattern":"artifact checksum mismatch","errorType":"http","errorClass":null,"httpStatus":502,"severity":"critical","filePath":"internal/pluginstore/direct.go","lineNumber":53,"sourceCode":"\tdata, errDownload := c.get(ctx, artifact.URL, \"application/octet-stream\", RequestKindArtifact, maxSize)\n\tif errDownload != nil {\n\t\treturn nil, errDownload\n\t}\n\tif maxSize > 0 && int64(len(data)) > maxSize {\n\t\treturn nil, fmt.Errorf(\"artifact exceeds declared size\")\n\t}\n\treturn data, nil\n}\n\nfunc VerifyArtifactChecksum(artifact Artifact, data []byte) error {\n\texpected := strings.ToLower(strings.TrimSpace(artifact.SHA256))\n\tif expected == \"\" {\n\t\treturn fmt.Errorf(\"artifact checksum missing\")\n\t}\n\tactualBytes := sha256.Sum256(data)\n\tactual := hex.EncodeToString(actualBytes[:])\n\tif actual != expected {\n\t\treturn fmt.Errorf(\"artifact checksum mismatch\")\n\t}\n\treturn nil\n}\n","sourceCodeStart":35,"sourceCodeEnd":57,"githubUrl":"https://github.com/router-for-me/CLIProxyAPI/blob/78f0c4079e3e6273d65d03b5549cffc898703264/internal/pluginstore/direct.go#L35-L57","documentation":"VerifyArtifactChecksum computes sha256 over the downloaded bytes and compares it (lower-cased, trimmed) to artifact.SHA256. 'artifact checksum mismatch' means the content served by the URL differs from the digest declared in the manifest. It is the direct-download integrity gate, analogous to error 600 but keyed on the artifact entry rather than a checksums map.","triggerScenarios":"DownloadArtifact output passed to VerifyArtifactChecksum where the digest differs: republished/rebuilt binary with a stale manifest sha256, corrupted or truncated download, or a redirect serving different content (e.g. a 'latest' URL that moved to a new build).","commonSituations":"Release pipeline rebuilt binaries but the plugin manifest was not regenerated; using mutable URLs (latest, /stable) instead of versioned artifact URLs; a mirror or proxy tampering with content.","solutions":["Re-download once to rule out transport corruption, then verify manually with sha256sum.","Update the manifest's sha256 to the digest of the currently served binary (or republish the correct old binary).","Pin artifact URLs to immutable, versioned release assets instead of mutable 'latest' links.","Automate manifest regeneration in CI so sha256/size are recomputed on every release."],"exampleFix":"// before\n// manifest built from v1.2.3, URL later repointed to v1.3.0 binary\ndata, _ := client.DownloadArtifact(ctx, artifact)\nerr := pluginstore.VerifyArtifactChecksum(artifact, data) // mismatch\n\n// after\n// version the URL and regenerate digest on release:\n//   url: https://example.com/p/1.3.0/p-linux-amd64\n//   sha256: <digest of the 1.3.0 binary>\ndata, _ := client.DownloadArtifact(ctx, artifact)\nerr := pluginstore.VerifyArtifactChecksum(artifact, data)","handlingStrategy":"try-catch","validationCode":"if strings.TrimSpace(artifact.SHA256) == \"\" {\n    return errors.New(\"cannot pre-verify: manifest sha256 missing\")\n}","typeGuard":null,"tryCatchPattern":"if err := pluginstore.VerifyArtifactChecksum(artifact, data); err != nil {\n    if strings.Contains(err.Error(), \"checksum mismatch\") {\n        // one retry with cache-bypass, then abort; never install unverified bytes\n        return fmt.Errorf(\"integrity failure for %s: %w\", artifact.Name, err)\n    }\n    return err\n}","preventionTips":["Pin versioned artifact URLs, never mutable 'latest' links.","Automate digest regeneration per release; verify with sha256sum when diagnosing."],"tags":["integrity","checksum","sha256","download","plugin-store"],"backgroundTag":null,"analyzedSha":"78f0c4079e3e6273d65d03b5549cffc898703264","analyzedAt":"2026-08-15T12:26:37.444Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}