{"record":{"id":"9e64b452f89deffb","repo":"tikv/tikv","slug":"download-method-only-accepts-single-file-requests","errorCode":null,"errorMessage":"download method only accepts single-file requests, use batch_download for multi-file requests","messagePattern":"download method only accepts single-file requests, use batch_download for multi-file requests","errorType":"validation","errorClass":"sst_importer::Error","httpStatus":null,"severity":"error","filePath":"src/import/sst_service.rs","lineNumber":1085,"sourceCode":"        };\n        self.threads.spawn(handle_task);\n    }\n\n    /// Downloads the file and performs key-rewrite for later ingesting.\n    fn download(\n        &mut self,\n        _ctx: RpcContext<'_>,\n        req: DownloadRequest,\n        sink: UnarySink<DownloadResponse>,\n    ) {\n        let label = \"download\";\n        IMPORT_RPC_COUNT.with_label_values(&[label]).inc();\n        let timer = Instant::now_coarse();\n\n        // download method only handles single file downloads\n        // Check that this is indeed a single-file request\n        if !req.get_ssts().is_empty() {\n            let error = sst_importer::Error::Io(std::io::Error::new(\n                std::io::ErrorKind::InvalidInput,\n                \"download method only accepts single-file requests, use batch_download for multi-file requests\",\n            ));\n            let mut resp = DownloadResponse::default();\n            resp.set_error(error.into());\n            let _ = sink\n                .success(resp)\n                .map_err(|e| warn!(\"send rpc response\"; \"err\" => %e));\n            return;\n        }\n        let importer = Arc::clone(&self.importer);\n        let download_speed_limiter = self.download_speed_limiter.clone();\n        let mem_limit = self.mem_limit;\n        let tablets = self.tablets.clone();\n        let start = Instant::now();\n        let resource_limiter = self.resource_manager.as_ref().and_then(|r| {\n            r.get_background_resource_limiter(\n                req.get_context()","sourceCodeStart":1067,"sourceCodeEnd":1103,"githubUrl":"https://github.com/tikv/tikv/blob/78aedc1c81ef3f7d8bacc6e9d09f56460f134937/src/import/sst_service.rs#L1067-L1103","documentation":"The legacy single-file download gRPC method rejects requests that carry a multi-file ssts set. The batch API (batch_download) exists for multi-file requests, so the single-file method guards against misuse and returns the error inside the DownloadResponse instead of failing the RPC.","triggerScenarios":"Invoking the download RPC with req.ssts non-empty (i.e. a DownloadRequest built for the batch API).","commonSituations":"BR/tooling code migrated to the batch API but still pointing at the download method; codegen or hand-written clients filling both the legacy single-file fields and ssts.","solutions":["Populate req.ssts and call batch_download instead","For a single file, use the single-file fields of the DownloadRequest with the download method","Fix client code that sets ssts unconditionally regardless of method"],"exampleFix":"// before\nreq.set_ssts(vec![meta]);\nclient.download(req).await?;\n// after: single-file request uses the single-file field\nreq.mut_sst().clone_from(&meta);\nclient.download(req).await?;\n// or, for multiple files:\n// client.batch_download(req_with_ssts).await?;","handlingStrategy":"validation","validationCode":"if !req.get_ssts().is_empty() {\n    return Err(\"use batch_download for multi-file requests\");\n}","typeGuard":null,"tryCatchPattern":"let resp = client.download(req).await?;\nif let Some(err) = resp.get_error() {\n    if err.get_message().contains(\"single-file\") {\n        return client.batch_download(req_for_batch).await.map_err(Into::into);\n    }\n    return Err(err.clone().into());\n}","preventionTips":["Branch client code on file count: one file -> download, many -> batch_download","Never populate ssts when calling the single-file download method","After migrating to the batch API, update all call sites","Check DownloadResponse.error and switch endpoints on api-misuse messages"],"tags":["rust","grpc","import","api-misuse","validation"],"backgroundTag":"wrong-rpc-endpoint","analyzedSha":"78aedc1c81ef3f7d8bacc6e9d09f56460f134937","analyzedAt":"2026-09-03T23:31:32.398Z","contentChangedAt":"2026-09-03T23:31:32.398Z","schemaVersion":2},"datasetVersion":"2026-09-11T07:07:21.782Z"}