diff --git a/backend/src/cyclone/scheduler.py b/backend/src/cyclone/scheduler.py index 2734f0f..3aac5cc 100644 --- a/backend/src/cyclone/scheduler.py +++ b/backend/src/cyclone/scheduler.py @@ -650,20 +650,21 @@ class Scheduler: def _download_and_parse( self, f: InboundFile, file_type: str, ) -> tuple[Path, str, int]: - """Download from MFT, run the right handler. Returns (path, parser, count). + """Run the right handler on one inbound file. Returns (path, parser, count). - Stub mode: ``f.local_path`` already points at the staged file - (set by ``SftpClient._list_inbound_stub``). Real mode: the - remote name is ``f.name`` and we round-trip through paramiko. + Both stub and real modes read from ``f.local_path`` — the + inbound file is already on disk: + * Stub mode: ``_list_inbound_stub`` points ``local_path`` at + the operator-dropped staging file. + * Real mode: ``_list_inbound_paramiko`` downloads each + ``listdir_attr`` entry into the local cache as part of the + listing pass. Re-reading from the MFT would require + ``SftpClient.read_file`` with a full remote path, which the + scheduler was passing just ``f.name`` for (i.e. a bare + filename at the SFTP root, not the inbound dir). Use the + cached bytes instead. """ - if self._sftp_block.stub: - # In stub mode the InboundFile already has a local_path; - # reading the staged bytes directly avoids the stub's - # remote-path semantics (which expect a full inbound path). - content = f.local_path.read_bytes() - else: - client = self._sftp_client_factory(self._sftp_block) - content = client.read_file(f.name) + content = f.local_path.read_bytes() text = content.decode("utf-8") handler = HANDLERS[file_type] parser_used, claim_count = handler(text, f.name)