obsidian-dataview icon indicating copy to clipboard operation
obsidian-dataview copied to clipboard

Global Error "Evaluation Error: TypeError: i.push is not a function"

Open jackreid opened this issue 1 year ago • 10 comments

What happened?

What happened?

When using dataviewjs, if I try and render a malformed dv.table on a particular markdown file, I will sometimes trigger a particular error. Once this error has been triggered, all my dataviewjs scripts across all my files in my vault will only return the same error, even though they normally work fine. Closing and reopening obsidian (or reloading without saving) turns the other scripts back to normal (until I try and render the malformed script).

Bug Description

**Step 1) ** Switch to read mode on a file with some malformed dv.table. For example:

dv.table(["Date Read", "Title"],
		topItemsdv.map(v => [
		v.author,
		v.count])
		);

Where topItemsdv is some dv.array.

Step 2) Get the following error:

Evaluation Error: TypeError: i.push is not a function  
    at l$1.diffed (plugin:dataview:15880:1539)  
    at j$2 (plugin:dataview:15878:6418)  
    at w$2 (plugin:dataview:15878:2236)  
    at j$2 (plugin:dataview:15878:6192)  
    at w$2 (plugin:dataview:15878:2236)  
    at L$1 (plugin:dataview:15878:7442)  
    at j$2 (plugin:dataview:15878:6377)  
    at w$2 (plugin:dataview:15878:2236)  
    at L$1 (plugin:dataview:15878:7442)
    at j$2 (plugin:dataview:15878:6377)

Step 3) Delete the error producing code (Bug occurs whether or not I do this). If I do so, the error on this particular page goes away, as expected.

Step 4) Close the error producing file.

Step 5) Open some other file in the same vault that has a normally functional dataviewjs script. For example:

var genre = "Philosophy";
var genre2 = "Ethics";
dv.header(1, 'Search');
var unread_pages = dv.pages('"01 Media/01 Books"')
    .where(p => p.title &&
			p.status == "read" &&
			(p.field == genre || p.topic == genre ||
			p.field == genre2 || p.topic == genre2))
			.sort(p => p.authors[0]);
dv.table(["Title", "Director", "Genre","Subgenre", "Series"],
		unread_pages.map(p => [
		//The Title
		p.file.link,
		//The Director(s)
		p.authors,
		//The Genre
		p.field,
		//The Subgenre
		p.topic,
		//The Series
		p.series
		])
);

Step 6) Switch to read mode and receive the following errror:

Evaluation Error: TypeError: i.push is not a function  
    at l$1.diffed (plugin:dataview:15880:1539)  
    at j$2 (plugin:dataview:15878:6418)  
    at w$2 (plugin:dataview:15878:2236)  
    at j$2 (plugin:dataview:15878:6192)  
    at w$2 (plugin:dataview:15878:2236)  
    at L$1 (plugin:dataview:15878:7442)  
    at j$2 (plugin:dataview:15878:6377)  
    at w$2 (plugin:dataview:15878:2236)  
    at L$1 (plugin:dataview:15878:7442)
    at j$2 (plugin:dataview:15878:6377)

Expectations

That an dataviewjs rendering error in one file should not cause errors in other, independent queries in other files, particularly after the error producing code is deleted.

Workaround

Either close and reopen obsidian or reload without saving.

Synopsis

One error replicates itself across all files in the vault

Environment

Using Obsidian v0.15.9 on Ubuntu 22.04.1 LTS Using Dataview v.0.5.41

DQL

No response

JS

var books = dv.pages('"01 Media/01 Books"')
    .where(p => p.title &&
		   	p.status == 'read').sort(p => p.dateRead);	


function authorsCount(bookSet) {
	let authorsDict = {};
	for (i=0; i < bookSet.length; i++) {
		var authors = bookSet[i].authors;
		for (j=0; j < authors.length; j++) {
			var author = authors[j];
	
			if (author in authorsDict) {
				authorsDict[author]++;
	
			} else{
				authorsDict[author] = 1;
			}
		}
	}
	return authorsDict;
}

var authorsDictFull = authorsCount(books);
var items = Object.keys(authorsDictFull).map(function(key) {
  return [key, authorsDictFull[key]];
});

// Sort the array based on the second element
items.sort(function(first, second) {
  return second[1] - first[1];
});


var topItems = items.slice(0,6);

var itemsArray = [];
for (i=0; i < topItems.length; i++) {
	var itemAuthor = topItems[i][0];
	var itemCount = topItems[i][1];
	itemsArray.push({author: itemAuthor,
						count: itemCount})
}


dv.paragraph(itemsArray);

var topItemsdv = dv.array(itemsArray);

dv.paragraph(topItemsdv);


dv.table(["Date Read", "Title"],
		topItemsdv.map(v => [
		v.author,
		v.count])
		);

Dataview Version

0.5.41

Obsidian Version

0.15.9

OS

Linux

jackreid avatar Aug 10 '22 15:08 jackreid

Can you give us a sequential set of steps to repro with?

AB1908 avatar Aug 10 '22 15:08 AB1908

I provided the code and the sequential steps above already. Is there something specific you want in addition? Through further experimentation, I have tried to find the simplest code that reproduces it. That is here:

var books = dv.pages('"01 Media/01 Books"')
    .where(p => p.title &&
		   	p.status == 'read').sort(p => p.dateRead);	

function authorsCount(bookSet) {
	let authorsDict = {};
	for (i=0; i < bookSet.length; i++) {
		var authors = bookSet[i].authors;
		for (j=0; j < authors.length; j++) {
			var author = authors[j];
	
			if (author in authorsDict) {
				authorsDict[author]++;
	
			} else{
				authorsDict[author] = 1;
			}
		}
	}
	return authorsDict;
}

var authorsDictFull = authorsCount(books);

var testArray = ["a", "b", "c"];
var testArraydv = dv.array(testArray);
dv.list(testArraydv)

Evidently it is something about the authorsCount() function that causes it. Will continue to investigate what is wrong with it, but still not sure why it causes a persistent bug that effects all my other files. As you can see, it doesn't matter if I am actually trying to print the output of authorsCount(), only that it has been run.

I will note that printing using dv.paragraph rather than dv.list works fine here.

jackreid avatar Aug 10 '22 15:08 jackreid

Ya I didn't expect it to be persistent. I doubt I have the chops to investigate it but I'll give it a try when I can.

AB1908 avatar Aug 10 '22 15:08 AB1908

Also attaching a screenshot of dv.paragraph(authorsDictFull) so you can see that the function is returning the dictionary that I would expect it to.

image

jackreid avatar Aug 10 '22 15:08 jackreid

Further experimentation has allowed me to find a more simple, standalone example that reproduces the bug without having to run a query.


var testDict = {a: 1,
				b: 2,
				c: 3}
				
var items = Object.keys(testDict).map(function(key) {
  return [key, testDict[key]];
});


var itemsArray = [];
for (i=0; i < items.length; i++) {
	var itemAuthor = items[i][0];
	var itemCount = items[i][1];
	itemsArray.push({author: itemAuthor,
						count: itemCount})
}


dv.list(items)

Not that dv.list is being applied to items, not itemsArray.

dv.paragraph(items) returns: image

dv.paragraph(itemsArray) returns: image

jackreid avatar Aug 10 '22 15:08 jackreid

This errors appears to be happening inside the minified code for one of the libraries, for some reason. Additionally, apparently the itemsAray actually affects whether or not this crashes, which makes absolutely no sense to me. What appears to be happening is this code somehow corrupts the library code, breaking all DataviewJS views.

blacksmithgu avatar Aug 11 '22 04:08 blacksmithgu

Well, this is tremendously annoying. The issue is your loop - it should be

for (var i=0; i < items.length; i++)

instead of

for (i=0; i < items.length; i++)

blacksmithgu avatar Aug 11 '22 06:08 blacksmithgu

Doing i=0 is overriding an esoteric, minified i variable inside some minified library code; this is because the global state is still implicitly available in the eval'd script (which I something I thought I fixed, though quite obviously not). Using var i or let i creates a local variable which avoids this problem, at least.

Not sure off the top of my head how to prevent this; probably going to require setting up shadowing of the window and global variables inside scripts to hide global state, which is going to break some scripts.

blacksmithgu avatar Aug 11 '22 07:08 blacksmithgu

Oh, wow, it didn't cross my mind that i could be in use elsewhere. Actually declaring it with var i or let i does indeed seem to work. Returning to the itemsArray example, the following resolved the issue:

var itemsArray = [];
let i;
for (i=0; i < items.length; i++) {
	var itemAuthor = items[i][0];
	var itemCount = items[i][1];
	itemsArray[i] = {author: itemAuthor,
						count: itemCount}
}

As does:

var itemsArray = [];
for (let i=0; i < items.length; i++) {
	var itemAuthor = items[i][0];
	var itemCount = items[i][1];
	itemsArray[i] = {author: itemAuthor,
						count: itemCount}
}

I admit that javascript is not my primary programming language so perhaps my original code was poorly written independent of this particular bug and the new versions are better practices. If so, I'm not sure that you actually need to change the backend code to prevent the bug. It might just be a matter of making the solution to the error visible on search engines, which this thread is a step towards.

Thanks so much for figuring this out! Dataview is by far and away my favorite Obsidian plugin.

jackreid avatar Aug 11 '22 12:08 jackreid

Yeah, a very valid workaround is to jkust make sure to use let/var/const in your variable definitions - I'll see what I can do about improving the error UX.

blacksmithgu avatar Aug 12 '22 01:08 blacksmithgu

So I stumbled across this thread when looking up the same error. (I ended up abandoning the first code that caused it to, as it was not fit for purpose) I had some code that worked until I added "i = 0" above and " i++" inside a "foreach" loop the shortest code I found to reproduce:


i = 0
for(let page of dv.pages.file.tasks){
 i++;
}

Norrun avatar Jun 27 '23 21:06 Norrun

What blacksmithgu was saying earlier that using i like that conflicts with obsidian's internal code. Declare it with a let.

AB1908 avatar Jun 28 '23 11:06 AB1908