Skip to content

Iterable file readers keep their state after a complete iteration: FolderBrowserTask, InputFolderBrowserTask, CsvReaderTask, LineReaderTask #208

Closed

Description

@njoubert-cleverage

Description

FolderBrowserTask, CsvReaderTask and LineReaderTask (and their Input* variants) do not reset their internal iterator or file once an iteration is finished. If the task runs again, for example because it receives a second input from an upstream iterable task, it does not start over:

  • FolderBrowserTask / InputFolderBrowserTask, same folder twice: the second pass logs a false "No item found" and outputs nothing.
  • InputFolderBrowserTask, two different folders: throws LogicException: Folder path '…/dB' already initialized with a different value …/dA, the folder path being only cleared on flush().
  • CsvReaderTask / InputCsvReaderTask, same path twice: the second pass outputs nothing (skipped).
  • LineReaderTask / InputLineReaderTask, same path twice: the second pass outputs false.

JsonStreamReaderTask, CsvSplitterTask and AbstractIterableOutputTask already release their state at the end of the iteration.

Reproduction

clever_age_process:
    configurations:
        demo.readers_reset.folder:
            tasks:
                folders:
                    service: '@CleverAge\ProcessBundle\Task\ConstantIterableOutputTask'
                    options:
                        output: ['/var/www/var/readers-reset/dA', '/var/www/var/readers-reset/dA', '/var/www/var/readers-reset/dB']
                    outputs: [browse]
                browse:
                    service: '@CleverAge\ProcessBundle\Task\File\InputFolderBrowserTask'
                    outputs: [debug_folder]
                debug_folder:
                    service: '@CleverAge\ProcessBundle\Task\Debug\DebugTask'
        demo.readers_reset.csv:
            tasks:
                csv_files:
                    service: '@CleverAge\ProcessBundle\Task\ConstantIterableOutputTask'
                    options:
                        output: ['/var/www/var/readers-reset/a.csv', '/var/www/var/readers-reset/a.csv']
                    outputs: [read_csv]
                read_csv:
                    service: '@CleverAge\ProcessBundle\Task\File\Csv\InputCsvReaderTask'
                    outputs: [debug_csv]
                debug_csv:
                    service: '@CleverAge\ProcessBundle\Task\Debug\DebugTask'
        demo.readers_reset.line:
            tasks:
                text_files:
                    service: '@CleverAge\ProcessBundle\Task\ConstantIterableOutputTask'
                    options:
                        output: ['/var/www/var/readers-reset/a.txt', '/var/www/var/readers-reset/a.txt']
                    outputs: [read_lines]
                read_lines:
                    service: '@CleverAge\ProcessBundle\Task\File\InputLineReaderTask'
                    outputs: [debug_line]
                debug_line:
                    service: '@CleverAge\ProcessBundle\Task\Debug\DebugTask'

Data: dA/f1.txt, dB/f2.txt, a.csv = id;name\n1;foo\n2;bar\n, a.txt = l1\nl2\n.

Output on main (c1a2804, PHP 8.5, Symfony 7.4):

Starting process 'demo.readers_reset.folder'...
"/var/www/var/readers-reset/dA/f1.txt"
WARNING   [cleverage_process_task] No item found in path /var/www/var/readers-reset/dA
CRITICAL  [cleverage_process_task] Folder path '/var/www/var/readers-reset/dB' already initialized with a different value /var/www/var/readers-reset/dA

Starting process 'demo.readers_reset.csv'...
array:2 [ "id" => "1"  "name" => "foo" ]
array:2 [ "id" => "2"  "name" => "bar" ]
Process 'demo.readers_reset.csv' executed successfully        <- second input produced nothing

Starting process 'demo.readers_reset.line'...
"l1\n"
"l2\n"
false                                                          <- second input
Process 'demo.readers_reset.line' executed successfully

Expected: dA/f1.txt, dA/f1.txt, dB/f2.txt; the two CSV rows twice; "l1\n", "l2\n" twice.

Cause

Proposed fix

Release the state in next() once the iteration is over, as JsonStreamReaderTask::next() already does:

  • FolderBrowserTask: reset $files to null;
  • InputFolderBrowserTask: override next() to also reset $folderPath (a different path received mid-iteration still throws);
  • CsvReaderTask: close the CSV file and reset it to null;
  • LineReaderTask: reset the SplFileObject to null.

The Input* variants inherit the fix: each new input, same path or not, is read from its beginning. Update the reference documentation, add unit tests.

Requirements

  • Documentation updates
    • Reference
    • Cookbooks
    • Changelog
  • Unit tests

Breaking changes

None.

Activity

  1. added a commit that references this issue on Sep 28, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions