Repository: bids-standard/bids-validator (language: python)

Issue:
Different validator outputs between webapp version and command line version
For the command line version, I'm using
node v22.6.0
npm 10.8.2
bids-validator 1.15.0

And bids-validator doesn't return any error or warning, while the webapp does -- on the same data.

I'm using data from https://github.com/PennLINC/CuBIDS/raw/main/cubids/tests/data/BIDS_Dataset.zip, but just included the dataset_description.json, README, and sub-02 folder.

In addition, when running the webapp version, I got this error:

The number of volumes in this scan does not match the number of volumes in the associated '.bvec' and '.bval' files.
/sub-02/ses-phdiff/dwi/sub-02_ses-phdiff_acq-HASC55AP_dwi.nii.gz

However, NVols = 61, bval has 61 values, and bvec is an array with the shape of 3x61, so this shouldn't be an error.



Discussion / hints:
I can look at this in a bit, but figure I'll ping @tsalo due to CuBIDS, as he might know something I don't.
And to be clear, are you using https://bids-standard.github.io/bids-validator/ or https://bids-standard.github.io/bids-validator/legacy/?
I'm using https://bids-standard.github.io/bids-validator/.
Okay, so the difference is that you're using the schema validator webapp and the legacy validator CLI.

We'll look at the data to determine if there's a bug in the schema validator or a problem with the check in the legacy validator.

Fix patch:
diff --git a/src/files/deno.ts b/src/files/deno.ts
index b6fe4b13..f3065a1f 100644
--- a/src/files/deno.ts
+++ b/src/files/deno.ts
@@ -159,7 +159,7 @@ export async function readFileTree(rootPath: string): Promise<FileTree> {
     )
     ignore.add(await readBidsIgnore(ignoreFile))
   } catch (err) {
-    if (err && typeof err === 'object' && !('code' in err && err.code !== 'ENOENT')) {
+    if (err && typeof err === 'object' && !('code' in err && err.code === 'ENOENT')) {
       logger.error(`Failed to read '.bidsignore' file with the following error:\n${err}`)
     }
   }
diff --git a/src/files/dwi.ts b/src/files/dwi.ts
index b7465b62..48136945 100644
--- a/src/files/dwi.ts
+++ b/src/files/dwi.ts
@@ -10,7 +10,7 @@ export function parseBvalBvec(contents: string): string[][] {
   // BVAL files are a single row of numbers, and may contain
   // trailing whitespace
   return normalizeEOL(contents)
-    .split(/\s*\n/) // Split on newlines, ignoring trailing whitespace
+    .split(/\n/) // Split on newlines
     .filter((x) => x.match(/\S/)) // Remove empty lines
-    .map((row) => row.split(/\s+/))
+    .map((row) => row.trimEnd().split(/\s+/)) // Split on whitespace, ignoring trailing
 }


Test patch:
diff --git a/src/files/dwi.test.ts b/src/files/dwi.test.ts
new file mode 100644
index 00000000..acb426a7
--- /dev/null
+++ b/src/files/dwi.test.ts
@@ -0,0 +1,35 @@
+import { assertEquals } from '@std/assert'
+
+import { parseBvalBvec } from './dwi.ts'
+
+Deno.test('Test bval/bvec parsing', async (t) => {
+  await t.step('Load 3 bvals', async () => {
+    const bvals = parseBvalBvec('0 1 2 \n')  // Typically ends with " \n"
+    assertEquals(bvals, [['0', '1', '2']])
+  })
+  await t.step('Load 3 bvals - missing newline', async () => {
+    const bvals = parseBvalBvec('0 1 2 ')
+    assertEquals(bvals, [['0', '1', '2']])
+  })
+  await t.step('Load 3 bvals - no spaces', async () => {
+    const bvals = parseBvalBvec('0 1 2')
+    assertEquals(bvals, [['0', '1', '2']])
+  })
+  await t.step('Load 3 bvecs', async () => {
+    const bvecs = parseBvalBvec('0 1 2 \n0 1 2 \n0 1 2 \n')
+    assertEquals(bvecs, [['0', '1', '2'], ['0', '1', '2'], ['0', '1', '2']])
+  })
+  await t.step('Load 3 bvals - missing newline', async () => {
+    const bvecs = parseBvalBvec('0 1 2 \n0 1 2 \n0 1 2 ')
+    assertEquals(bvecs, [['0', '1', '2'], ['0', '1', '2'], ['0', '1', '2']])
+  })
+  await t.step('Load 3 bvals - no spaces', async () => {
+    const bvecs = parseBvalBvec('0 1 2\n0 1 2\n0 1 2\n')
+    assertEquals(bvecs, [['0', '1', '2'], ['0', '1', '2'], ['0', '1', '2']])
+  })
+  await t.step('Load 3 bvals - no spaces, missing newline', async () => {
+    const bvecs = parseBvalBvec('0 1 2\n0 1 2\n0 1 2')
+    assertEquals(bvecs, [['0', '1', '2'], ['0', '1', '2'], ['0', '1', '2']])
+  })
+})
+