Skip to content

[p5.js 2.0+ Bug Report]: loadTable() silently drops the last row of a single-column CSV with no trailing newline #9239

Description

@vedant21-oss

Most appropriate sub-area of p5.js?

  • IO

p5.js version

2.3.2 (verified on main @ 94fb07d)

Web browser and version

Not browser-specific — the defect is in src/io/csv.js. Reproduced by importing the module directly in Node v25.9.0.

Operating system

All

Steps to reproduce this

Steps:

  1. Save a single-column CSV with no trailing newline, e.g. the three bytes a\nb\nc written as name, Ada, Grace on three lines with no final \n.
  2. Load it with loadTable().
  3. The last row is missing, with no error or warning.

Snippet:

// Reproduces directly against the module, no browser needed:
import { parse } from './src/io/csv.js';

parse('a\nb\nc');    // [["a"], ["b"]]          <- "c" is gone
parse('a,b\nc,d');   // [["a","b"],["c","d"]]   <- multi-column is unaffected

Via the public API:

let table;

async function setup() {
  createCanvas(100, 100);
  // data.csv contains:  name\nAda\nGrace   (no trailing newline)
  table = await loadTable('data.csv', ',', 'header');
  print(table.getRowCount());  // 1, expected 2
}

Cause

The tokenizer flushes the pending value at end of input only when ctx.entry is non-empty:

// src/io/csv.js:137
// flush the last value
if (ctx.entry.length !== 0) {
  valueEnd(ctx);
  entryEnd(ctx);
}

For a single-column file, entryEnd() resets ctx.entry to [] at every newline. So when the file does not end with a newline, the final value is still sitting in ctx.value while ctx.entry is empty, and the flush is skipped entirely. The row is discarded silently.

Multi-column files are unaffected, because at least one value has already been pushed into ctx.entry by the separator before the loop ends. That is why this has gone unnoticed — the common case works.

Expected behaviour

parse('a\nb\nc') should return [["a"], ["b"], ["c"]], and loadTable() should return every row in the file regardless of whether it ends with a newline. Trailing newlines are optional in RFC 4180, and plenty of tools and hand-edited files omit them.

Suggested fix

   // flush the last value
-  if (ctx.entry.length !== 0) {
+  if (ctx.entry.length !== 0 || ctx.value !== '') {
     valueEnd(ctx);
     entryEnd(ctx);
   }

A fixture in test/unit/io/loadTable.js with a single-column file and no trailing newline would lock this in.

I'd be glad to open a PR with the fix and that test if this looks right to you.

Activity

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

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions