Roll20 uses cookies to improve your experience on our site. Cookies enable you to enjoy certain features, social sharing functionality, and tailor message and display ads to your interests on our site and others. They also help us understand how our site is being used. By continuing to use our site, you consent to our use of cookies. Update your cookie preferences .
×
Create a free account

[API] "Firebase.child failed" Error - Help Please!

March 24 (11 years ago)

Edited March 31 (11 years ago)
Note to mods: The API subforum appears to be for working scripts or general tips, so I'm putting this here. If I got it wrong: Sorry... can you move it for me?

Note to readers: See this post for the true root of the problem, and this post for the solution.


Hi! I have been making a script and I'm completely stumped. I'll start out with the error I'm getting in case it turns out it's actually quite common and someone can point me in the right direction just from this.

The error message:

/home/symbly/www/d20-api-server/node_modules/firebase/lib/firebase-node.js:1
orts, require, module, __filename, __dirname) { function f(a){throw a;}var j=v
^
Error: Firebase.child failed: First argument must be a non-empty string and can't contain ".", "#", "$", "[", or "]".
at Error (<anonymous>)
at Ha (/home/symbly/www/d20-api-server/node_modules/firebase/lib/firebase-node.js:12:204)
at G.W.H (/home/symbly/www/d20-api-server/node_modules/firebase/lib/firebase-node.js:126:213)
at TrackedObj._doSave (
(The arrow on the third line is actually pointing at "a" from "throw a").

It kind of seems like it's an error with reporting my actual error though, so I'm not holding out a lot of hope for that.

Here's what I'm trying accomplish:

I want to make an API script that does what this thread describes: https://app.roll20.net/forum/post/705674/cards-tha...

The script should use a page called "Inventory" to track your card objects. When you first make a token from a card, a copy appears on the Inventory page. Any changes that you make to your token are saved to the Inventory version when the token is removed, and when you next bring up that card's token, it loads everything from the Inventory copy.

Here's a breakdown of what's happening instead:

I have discovered that if I restart my script after creating the inventory copy, then everything works. However, if I don't then I consistently get the above error message when I try to take the card back into my hand (ie, upon saving... but after all my code runs). (Note that changes aren't actually saved, either).

(Note: I never got as far as making sure the functions are all called in the right place, so if you're going to be helping me with this, then make sure you always drag the card to your hand first, and only then to the table.)

(Note 2: I have a more complicated version of this script that uses the first line of the GM Notes field instead of the name. It fails in the exact same way.)

Current theories: (I may keep this section updated)

Because restarting the script fixes it, there is an issue with the event binding that is happening when I start the script? ie, the "on" lines?

And/or, because it happens after my save, I am leaving the object in a bad state? But... only if I don't restart the script? I expect that the system is running my code to make the changes, but then failing to actually implement the state change (my changes sure aren't getting saved, even though the code to make the changes all appears to run without error.)



I will include my actual code and the error messages in a different post, to keep this first post as concise as possible.
March 24 (11 years ago)

Edited April 07 (11 years ago)
I have posted the script and two sets of logs here: [Link removed, it's a lot of text that just makes it harder to scroll to the solution below]

The "success log" is the one I get when I follow these steps:

Bring card to hand
Bring card to table
Make changes & Restart script (in either order)
Bring card to hand
- SUCCESS

The "failure log" is the one I get when I follow these steps:

Bring card to hand
Bring card to table
Make changes
Bring card to hand
- FAILURE

As you can determine yourself, if you do a diff of the two, the only differences are:
  • The card token ID (since I have recalled & shuffled the deck)
  • The X and Y coordinates (where I drag the card onto the desktop)
  • The "restarting the sandbox" messages in the middle, with the page ID re-logged.
  • The error message at the end.
PS.

Prior to this, I had to create a deck and put a card in it. I only have one card, in case that matters. It's not an infinite deck.

I also have a page called "Inventory". I have been doing most of the testing while open to that page.
March 24 (11 years ago)
Ahem. You know what I should have done? I should have searched this error on the forums.

https://app.roll20.net/forum/post/669025/help-erro...
https://app.roll20.net/forum/post/527404/bug-tryin...
https://app.roll20.net/forum/post/521168/suggestio...

It's a known bug that you can't create an object and then make changes to it without restarting the script.

That said, I don't see any advice as to what to actually do about it? Is there anything short of deploying it with the warning "restart the script every time you grab a new card"?
March 30 (11 years ago)
I have no idea how to work around this bug, as it's affecting me as well. I've created a barebones test case that reproduces this issue here:

on("ready", function() {
    var n = +new Date();
    var obj = createObj("rollabletable", {
        name: String(n)
    });
    obj.set("name", "post_" + n);
});
Hopefully the devs will take a look at this, although this problem seems to have first been reported 3 months ago.
March 30 (11 years ago)
I've cataloged the various methods I've thought to try in this gist: https://gist.github.com/sirpengi/9865278
and they all reproduce this bug.
March 30 (11 years ago)
Just tried it on the dev server to see if that was the problem, no luck. I have narrowed down the issue to the obj.doSave (or possibly obj._doSave) method, as the following will quelch the error:

on("ready", function() {
    var n = +new Date();
    var obj = createObj("rollabletable", {
        name: String(n)
    });
    
    var old_doSave = obj.doSave;
    obj.doSave = function() {
        log("before");
        log(arguments);
        //old_doSave.apply(obj, arguments);
        log("after");
    }
    
    obj.set("name", "post_" + n);
});
The side effect though, is nothing gets saved. At least this narrows down where the problem is.
March 30 (11 years ago)
Success, I've figured out what the problem is, and I've created a workaround that, at least, lets me work with newly created objects. Here's the barebones implementation, basically the problem is the _fbpath value on new objects created is set to https://roll20-2.firebaseio.com/campaign-<campaign_id>-<somehash>/<_type>/-<somehash> but the fbpath value on objects you fetch look like /<_type>/-<somehash>. Basically you can work around this by setting the fbpath yourself after stripping off all that prefixes:

on("ready", function() {
    var n = +new Date();
    var obj = createObj("rollabletable", {
        name: String(n)
    });
    var p = obj.changed._fbpath;
    var new_p = p.replace(/([^\/]*\/){4}/, "/");
    obj.fbpath = new_p;
    obj.set("name", "new_" + n);
});

Probably might be a security concern too. Not sure if the fbpath I'm providing is checked anywhere. If I knew your IDs and some hash values perhaps I could overwrite objects belonging to someone else.
March 30 (11 years ago)
Lithl
Pro
Sheet Author
API Scripter

Changing the protected properties of a read-only R20 object ought to be overwritten the next time that object is read from the DB; I haven't specifically tested this with the fbpath, but I have with things like an object's ID in the past. However, in this case, you're changing the protected property of a newly-created object, to be the value that you want it to be when you later read it from the DB. If this bug is only occurring on new objects (in other words, if var obj = create(...); followed by obj = getObj(..., obj.id); in a new sandbox session works without problems), then this workaround shouldn't ever cause any issues.

March 30 (11 years ago)
Wow, you've actually done it! I'm a little confused as to why you can change this one property and not any others, but I can confirm that this works on my test case as well. I've moved your code into its own function for the easiest implementation by others, here's the function:

function fixNewObject(obj)
{
var p = obj.changed._fbpath;
var new_p = p.replace(/([^\/]*\/){4}/, "/");
obj.fbpath = new_p;
return obj;
}

and a very simple example of its use

on("ready", function()
{
    var o = createObj("text", { pageid: Campaign().get("playerpageid"), layer: "objects",
        left: 70, top: 70, width: 70, height: 70, text: "Newly created text."
    });
    o = fixNewObject(o);
    o.set( {text: "Updated text." });
});

So, would it be spamming to post this fix on a few of the more recent instances of this question? It's just that I think this is a very important discovery and I wouldn't want anyone to miss it.
March 30 (11 years ago)
Lithl
Pro
Sheet Author
API Scripter

Rendelle F. said:

Wow, you've actually done it! I'm a little confused as to why you can change this one property and not any others, but I can confirm that this works on my test case as well.

It is technically possible to change the other protected attributes. As I said, though, changes to protected attributes will only last for the current instance of the sandbox.

March 30 (11 years ago)
I meant without triggering the error that we were trying to circumvent. Why didn't this change also cause the Firebase error? I see that Shu Zong changed it a different way... and I didn't know you could change attributes without "set" at all. So I'm a bit lost as to how this works. :)
March 31 (11 years ago)

Edited April 07 (11 years ago)
As an actual solution, I'd prefer a fix in this method:

(function() {
    var oldCreateObj = createObj;
    createObj = function() {
        var obj = oldCreateObj.apply(this, arguments);
        if (obj && !obj.fbpath) {
            obj.fbpath = obj.changed._fbpath.replace(/([^\/]*\/){4}/, "/");
        }
        return obj;
    }
}())


This way, you just continue to use the createObj method as normal, and one they fix this bug you can just remove this snippet and then things will be fine. (In fact, I'm pretty positive that things will still work with this snippet in place even if they fix the bug. Leaving it in will just slow down createObj calls by a tiny amount). I've also wrapped it in an immediately-invoked function expression so as to not pollute the global scope. You should consider this the version to use to patch this problem.
March 31 (11 years ago)
Regarding how this fixes the firebase error, there's a difference between the attributes of that instance in the context of the javascript code, and the attributes of that object represented by that instance. In this post I'll assume the difference between the "instance" and the "object" is that you have access to the "instance" in your code (var obj = createObj(..), in this case obj is the instance), and you an only make modifications to the "object" (that is, the abstract Character, or Attribute, or RollableTable, etc that it represents) through the methods given to you on the "instance".

Thus, you can modify the instance however you like (this is javascript after all, a very dynamic language), but if you want to modify the object it represents, you have to use the api it provides (that is, use the obj.set and obj.get methods).

The problem is likely there's a bug somewhere in the roll20 api server code that doesn't handle newly created objects correctly. The fix I put in doesn't actually modify the object, it makes a change to the instance such that it works around the bug in the roll20 code. I have a feeling the roll20 code is supposed to set a proper obj.fbpath, and it currently just copies it over from the obj.changed._fbpath value, when it should strip out the domain and campaign hash prefixes. Luckily, by attaching the fbpath attribute myself, I've somehow circumvented that buggy behavior.
March 31 (11 years ago)
Makes sense, thank you.

I have updated my original post to direct people to your solution. Hopefully everyone that runs into this will find their way here.
April 07 (11 years ago)
I updated my solution with a small tweak so that error reporting works when you pass in the wrong values to createObj.
wow that is truly a great find and solution. very much appriciated!
April 25 (10 years ago)
Fabio M.
KS Backer
Awesome job, thanks a lot!
May 18 (10 years ago)
Hello - I applied this snippet to the top of my code but after a few runs I'm still getting the errors. Did I miss something in how this should be used?
May 27 (10 years ago)
Casey
Sheet Author

Shu Zong C. said:

As an actual solution, I'd prefer a fix in this method:
<snip>

I ran into this bug as well, and this workaround solved it for me. Thanks to everyone involved.... though why this bug still exists after months I can't fathom.
This bug persists after using the function above, and I'm getting a "TypeError: Cannot read property '_fbpath' of undefined at createObj (evalmachine.:567:37) at evalmachine.:285:7 at eval (

I've tried both the fixNewObject method by Rendelle and the new createObj function by Shu Zong. I have an on:ready, add:character function that pulls defined attributes from an array and creates the attributes on any new character created. My code is thus (how do you format your code as above?).

on("ready", function() {
if (!state.dcc) {
state.dcc = {};
tmp = "Created state.dcc: " + state.dcc; log(tmp);
} else {
tmp = "state.dcc: " + state.dcc; log(tmp);
};
// remove "if" to redefine state.dcc.sheetAttributeArray, if necessary
if (!state.dcc.sheetAttributeArray) {
state.dcc.sheetAttributeArray = [
// basics
"Level","XP","AC","HP","INIT","Speed",
// ability scores & mods
"Strength","STR",
"Agility","AGI",
"Stamina","STA",
"Personality","PER",
"Intelligence","INT",
"Luck","LCK",
// saves
"REF","FORT","WILL",
// dice, miscellaneous
// "ActionDie","DeedDie","ATK","CritDie","Disapproval","Momentum",
// coins
"PP","EP","GP","SP","CP"];
tmp = "Created state.dcc.sheetAttributeArray: " + state.dcc.sheetAttributeArray; log(tmp);
};
if (!state.dcc.abilityScoreArray) {
state.dcc.abilityScoreArray = [["Strength","STR"],["Agility","AGI"],["Stamina","STA"],["Personality","PER"],["Intelligence","INT"],["Luck","LCK"]];
tmp = "Created state.dcc.abilityScoreArray: " + state.dcc.abilityScoreArray; log(tmp);
};

on("add:character", function(obj) {

for(i = 0; i < state.dcc.sheetAttributeArray.length; i++) {
log(state.dcc.sheetAttributeArray[i]);
log(getAttrByName(obj.id,state.dcc.sheetAttributeArray[i]));
createObj("attribute", {
name: state.dcc.sheetAttributeArray[i],
current: "0",
characterid: obj.id
});
};
});
});
June 26 (10 years ago)
Shu Zong C. you are the man. I spent forever trying to figure this out. Created another script(that I will delete later) with
(function() {
    var oldCreateObj = createObj;
    createObj = function() {
        var obj = oldCreateObj.apply(this, arguments);
        if (obj && !obj.fbpath) {
            obj.fbpath = obj.changed._fbpath.replace(/([^\/]*\/){4}/, "/");
        }
        return obj;
    }
}())
July 03 (10 years ago)
Steve D.
Sheet Author
API Scripter
I tried using this script and it did keep the system from throwing an error however if I dynamicly add attributes to a character sheet and close the campaign window nothing is saved. Is there a work around?
July 30 (10 years ago)

Steve D. said:

I tried using this script and it did keep the system from throwing an error however if I dynamicly add attributes to a character sheet and close the campaign window nothing is saved. Is there a work around?

I just tried to replicate your issue. I created a new attribute through the API and used the workaround function to fix it, then closed the campaign and reopened it. The attribute (and the value I set for it) was still there.

Perhaps provide the code you're using for creating the new object? Maybe there's something funky going on there (assuming you haven't resolved your issue in the subsequent 3 weeks since you posted it).
August 14 (10 years ago)
Steve D.
Sheet Author
API Scripter
Thanks for your help. I was able to fix the problem.

The issues was once a character was newly created in game via GUI and then the api tries to change an attribute of that character it was throwing an error. There was no obj.change to modify and set the fbpath so I needed to create my own. This only solves this problem for type=attribute. Other type version would need to be created.

(function() {
    var oldCreateObj = createObj;
    createObj = function() {
        var obj = oldCreateObj.apply(this, arguments);
        if (obj && !obj.fbpath) {
            	obj.fbpath = obj.changed._fbpath.replace(/([^\/]*\/){4}/, "/");
        } else if (obj && !obj.changed && type == 'attribute') {           
		obj.fbpath = '/char-attribs/char/'+ characterID +'/'+ id;
	}

        return obj;
    }
}())

August 29 (10 years ago)
Steve D., that worked perfectly, and I didn't have to change anything other than dropping the function at the top of my script. Beautimus!