429 Status Code - Post Request Error

272 views
Skip to first unread message

Alex Bishka

unread,
May 21, 2023, 7:57:22 PM5/21/23
to Chromium Extensions
Hi there,

I apologize if this is a noob question - I'm new to Chrome extensions - but I'm having some difficulty modifying a web page with some content scripts.

This is the error that occurs:
```
POST https://o33249.ingest.sentry.io/api/4504238875803648/envelope/?sentry_key=33f79e998f93410882ecec1e57143840&sentry_version=7&sentry_client=sentry.javascript.nextjs%2F7.50.0 429
```

This is the only thing that I see that is going wrong, hasn't been great to work with.

From console logs I know that my content.js file is running, specifically the function that modifies the html document. However, the html document itself does not get updated - going into the web page's html code there is no change, even after the last occuring console log of my content script.

Here is the relevant code from my content.js that is responsible for the creation of the html element, grabbing document and then updating the document:
```
// my html code that I want to insert
const myButton = document.createElement("button")
myButton.innerHTML = "Click me"
myButton.addEventListener('click', function() {
       alert('Button was clicked!');
});

// code to grab and update the page
const htmlPage = document.getElementsByClassName("some_class")
if (htmlPage.length) {    // may not exist
        const lastComponent = htmlPage[htmlPage.length - 1]
           
        console.log(lastComponent)    // this successfully prints out the last component and I can click it on the console to navigate to where it exists in the web page

        // insertion code
        const subDiv = lastComponent.childNodes[1]
        subDiv.insertBefore(myButton, subDiv.firstChild)

        console.log(subDiv)     // logs the updated html correctly
}
```

I've also tried re-grabbing the document after updating the html code and logging the piece I'm changing - the console log shows that this is identical to the result of the subDiv being logged above.

Additionally, if I run my js code in the console the button is successfully added.

I've hit a wall here and can't find anything super useful related to the error. Any help would be greatly appreciated. Please let me know if you need more information from my side!

Cuyler Stuwe

unread,
May 21, 2023, 8:34:11 PM5/21/23
to Alex Bishka, Chromium Extensions
That error message is presenting itself as a rate-limiting error from Sentry (an error-logging API).
It's not related (at least, not directly) to any modifications you're making to the markup of a page.

Looking up various tokens in the request for a match, 4504238875803648 seems like you're trying to do something with ChatGPT.
I get this same request/response when I look at the devtools immediately after signing in, regardless of what I have installed.

I'm a little worn out from this latest AI bubble, but a thought came to mind:
"You seem to be enthusiastic about AI, given that you're building something that's meant to leverage it to solve problems. Have you attempted to ask ChatGPT this problem yet?"

--
You received this message because you are subscribed to the Google Groups "Chromium Extensions" group.
To unsubscribe from this group and stop receiving emails from it, send an email to chromium-extens...@chromium.org.
To view this discussion on the web visit https://groups.google.com/a/chromium.org/d/msgid/chromium-extensions/355770b2-68b1-4136-a8dc-5eeab2e21d25n%40chromium.org.

Alex Bishka

unread,
May 21, 2023, 8:46:54 PM5/21/23
to Chromium Extensions, Cuyler Stuwe, Chromium Extensions, Alex Bishka
Haha yea I was trying to mess around with GPT.

I have been asking GPT about this, but it hasn't been the most helpful with chrome extension development, given its cutoff in 2021, it tends to respond with v2 answers (even with browser mode enabled). But in this case I think the issue is so vague that I can't seem to give it enough context to provide a helpful answer. I was also looking at some YouTube tutorials on v3, but they didn't have issues when updating the page. I've hit a nice brick wall it seems, so I'm here now.

You are right about the error as well, I used devtools on a different browser and got the same result.

Is it possible some websites don't allow modifications? Is there something else afoot here?

Alex Bishka

unread,
May 21, 2023, 9:30:41 PM5/21/23
to Chromium Extensions, Alex Bishka, Cuyler Stuwe, Chromium Extensions
Could it be that my code is running before the page fully updates/renders? Or something to that effect?

I've been messing around with the following two ways to call my content.js script from background.js:

```
chrome.tabs.onUpdated.addListener((tabId, tab, changeInfo) => {
    if (tab.url && tab.url.includes("chat.openai.com")) {
        try {
            chrome.scripting.executeScript({
                target: {tabId: tabId},
                files: ['scripts/content.js']
            })
        } catch (error) {
            console.log("error in tab listener:")
            console.log(error)
        }
    }
});
```

and

```
chrome.tabs.onUpdated.addListener((tabId, tab, changeInfo) => {
    if (tab.url && tab.url.includes("chat.openai.com")) {
        try {
            chrome.tabs.sendMessage(tabId, {
                type: "NEW",
                videoId: 2
            })
        } catch (error) {
            console.log("error in tab listener:")
            console.log(error)
        }
    }
});
```

In both cases, content.js seems to run, but the original behavior still occurs. I have activeTab and scripting both in my permissions, as well as my content.js file listed in my content_scripts section (with the appropriate matching site).

Cuyler Stuwe

unread,
May 21, 2023, 9:56:14 PM5/21/23
to Alex Bishka, Chromium Extensions
You originally framed your question by asking how to prevent a network call that resulted in an error. Beyond that, you haven’t made it clear what the “original behavior” you’re trying to change is.

If your markup isn’t appearing, maybe you’re doing something wrong. Maybe the code on the page is removing it. Maybe something else, depending on how you’re trying to do it (e.g., a CSP that prevents “eval”, etc.).

If your markup is there in the code but isn’t appearing visually, maybe the CSS that applies is preventing you from seeing what you would otherwise expect.

I know everyone thinks they’re doing some revolutionary thing with ChatGPT that they can’t afford to tell anyone about, but by the time you’re asking people to help you for free, you need to do most of the legwork of providing as much context as possible.

Alex Bishka

unread,
May 21, 2023, 10:26:23 PM5/21/23
to Chromium Extensions, Cuyler Stuwe, Chromium Extensions, Alex Bishka
My apologies,

I am not really trying to obscure anything here - I'm new to extensions and barely have any code. I was also under the impression the code with the comments I pasted was clear enough, but I'll try and be more clear with my intended behavior. The reason I pasted that error is because I thought that was the problem.

My goal is just to add a button next to the copy button that is shown in the ChatGPT portal, and when clicked, if it could say "hello world" that would be good enough for now. From my understanding this should occur every time GPT responds to your question. So I guess it would be nice if I could change the markup on an initial load of a conversation and also as the conversation continues (the latter of which I can't even get code to trigger for).

And I guess for full details here is the full code that edits the markup (I kept it slightly simpler above):

```
// creating our button
const createPostBtn = document.createElement("button")
createPostBtn.innerHTML = "Click me"
createPostBtn.addEventListener('click', function() {

    alert('Button was clicked!');
});

createPostBtn.onmouseover = function() {
    this.style.color = 'black'; // Color when mouse hovers over
}
createPostBtn.onmouseout = function() {
    this.style.color = 'gray'; // Color when mouse is not hovering
}

// grabbing chat boxes in GPT
const chatBoxes = document.getElementsByClassName("flex p-4 gap-4 text-base md:gap-6 md:max-w-2xl lg:max-w-xl xl:max-w-3xl md:py-6 lg:px-0 m-auto")
if (chatBoxes.length) {    // may not have chats yet
    console.log("chat box len:")
    console.log(chatBoxes.length)

    const lastChat = chatBoxes[chatBoxes.length - 1]
   
    console.log("last chat:")
    console.log(lastChat)

    const btnsDiv = lastChat.childNodes[1].childNodes[1].childNodes[1]
    btnsDiv.insertBefore(createPostBtn, btnsDiv.firstChild)

    console.log("btns div:")
    console.log(btnsDiv)

    console.log("checking if doc is updated")
    console.log(document.getElementsByClassName("flex p-4 gap-4 text-base md:gap-6 md:max-w-2xl lg:max-w-xl xl:max-w-3xl md:py-6 lg:px-0 m-auto")[chatBoxes.length - 1].childNodes[1].childNodes[1].childNodes[1])
}
```

Like I said, this code works if I run it in my browser's console. The button is added right next to the copy icon/button on GPT's responses and when I press it, the alert appears. I just can't quite figure why it doesn't work when I run the extension, especially since I'm not seeing any errors or messages other than the network one already mentioned.

I'm happy to provide more context, but I don't know what is considered relevant, as I am new to extensions. So, please explicitly ask me for what you need from me and I will be more than happy to provide more details.

I also don't particularly think what I'm doing is revolutionary, I'm just trying to learn how to use plug-ins and I thought it would be fun to make something that could make my life easier in regards to GPT use - but this is mostly exploratory.

Cuyler Stuwe

unread,
May 22, 2023, 4:16:29 AM5/22/23
to Chromium Extensions, Alex Bishka, Cuyler Stuwe, Chromium Extensions
Timing is a common problem in extensions that modify the DOM.
If you run your code within an obnoxiously-long setTimeout callback, does it work? If so, your code is probably running to early.
Are your logs all logging everything you expect them to?

For what it's worth, though... The code you provided isn't working even in the devtools for me:

Screenshot 2023-05-21 at 11.53.37 PM.png

Another thing to note: The methods you're using for selecting "chat box" nodes seem fairly fragile.
If a single one of those attributes changes even slightly, the selector will break.

The page doesn't give you a whole lot of identifying information to work with, but this selector seems to pick out the same "chat box" elements with less complexity:
const chatBoxes = document.querySelectorAll('.z-0 > .overflow-hidden .group')

Then you could probably do something like:
const lastChat = chatBoxes.pop();
const btnsDiv = lastChat.querySelector('div:has(> button:only-of-type~div)');

The way I'm seeing the site on my machine, the indexing is a little off in your version of this. You'd have to change your btnsDiv code to:
const btnsDiv = lastChat.childNodes[1].childNodes[1].childNodes[0];

Caveats:
  • There's always the possibility that ChatGPT is A/B testing, serving regional/setting-specific variants, etc., and that this may not work for you.
  • It's also possible that there's more than one issue at play.

Cuyler Stuwe

unread,
May 22, 2023, 4:16:57 AM5/22/23
to Chromium Extensions, Cuyler Stuwe, Alex Bishka, Chromium Extensions
Also:

*too

Alex Bishka

unread,
May 22, 2023, 11:59:41 AM5/22/23
to Chromium Extensions, Cuyler Stuwe, Alex Bishka, Chromium Extensions
I ended up switching to your code (with the exception of the pop call as that was causing errors) as it also worked on my machine, and it's much cleaner.

I've added a setTimeout to the code and a screenshot of what it currently looks like (apologies for that last log not directly logging the div - running out of time as I need to get to work). I sleep for 10 seconds during this (anything higher than that usually resulted in a connection error, which was also strange to me). I do find it weird that in both the before and the after logging of the btnsDiv the button has been inserted into the div. That behavior seems really weird to me. Additionally my last console log that prints the last chat box does not show the update div with the new button inside of it.

Sorry for not being able to dive into it more at the moment, but it does seem like something is going wrong with insertion, or maybe the page is preventing me in some way.
Screenshot 2023-05-22 at 8.55.47 AM.png

Cuyler Stuwe

unread,
May 22, 2023, 2:08:54 PM5/22/23
to Chromium Extensions, Alex Bishka, Cuyler Stuwe, Chromium Extensions
I didn't convert the result of .querySelectorAll into an array for you in my example code. If you convert that into an array, you'll get a .pop() method that gives you a nice clean way of grabbing the last element without needing to run calculations against the length. It's not necessary to do it this way though. The way your code grabs the last element selected should work just fine.

Question: Why are you inserting before the buttons div? Seems like you'd want to insert within that div instead. I'm not looking at it right now, but I imagine that the structure/styles are not set up for your button to be visible on-page if inserted outside of that container.

I would prefer to do something like e.g.:

btnsDiv.insertAdjacentHTML("afterbegin", `<button id="putSomethingHereYouCanSelectLater">This is a button</button>`);

Cuyler Stuwe

unread,
May 22, 2023, 3:03:45 PM5/22/23
to Chromium Extensions, Cuyler Stuwe, Alex Bishka, Chromium Extensions
Throwing it together real quick as POC, this code right here...

setTimeout(() => {
    const chatBoxes = [...document.querySelectorAll((`.z-0 .overflow-hidden .group`))];
    const lastChatBox = chatBoxes?.pop();
    const buttonsDiv = lastChatBox.querySelector(`div:has(> button:only-of-type~div)`);
    buttonsDiv?.insertAdjacentHTML('afterbegin', `<button id="">I injected this.</button>`);
}, 10000);


... worked for me:

Screenshot 2023-05-22 at 11.56.31 AM.png

Maybe you can compare your code to what's provided there to see what you're doing differently.

Alex Bishka

unread,
May 23, 2023, 12:01:58 AM5/23/23
to Chromium Extensions, Cuyler Stuwe, Alex Bishka, Chromium Extensions
Regarding your question, I thought what was going on was that I had grabbed the div that contains all of the buttons and then inserted my button as the first element of that div. The console logs when I was testing seemed to support my thought, but strange. It's possible I've just bungled it up, I'm not experienced at all with raw html and manipulating documents (most of my frontend web experience comes through using frameworks).

Your code is far cleaner and so I have switched to it - thank you for putting it together along with the PoC. I can get the simple button injected correctly now. Thanks a lot for that, it has saved me a lot of head banging and tutorial scraping.

I have some extra questions below, if you don't mind answering them (no worries if you don't have the time):
I have to say I am confused by a few things, looking at your PoC. Is there no need for anything in the serviceWorker.js file? I would have thought it might be desired to have the serviceWorker.js message the content script depending on certain events. Is the serviceWorker.js file considered better practice than background.js?

Additionally, why isn't anything within content.js wrapped around a chrome api call, like chrome.runtime.onMessage.addListener? Is this because this is an example of injecting with static declarations? Then for potential future needs that are different, I could simply call a different content script? I'm confused here as the tutorial I was following on YouTube and the GPT answers (although those were very meh) I got suggested that.

Simeon Vincent

unread,
May 23, 2023, 1:10:19 AM5/23/23
to Alex Bishka, Chromium Extensions, Cuyler Stuwe
Is the serviceWorker.js file considered better practice than background.js?

I think you're describing two sides of the same coin. For many years the common pattern for declaring the background script of your extension was to name that file "background.js". In Manifest V3, background pages were replaced by service workers. It's still very common for extension developers to name the file that contains the service worker's logic "background.js", but some developers prefer to use other names like "service-worker.js".

{
  "name": "An example extension",
  "version": "1.0",
  "manifest_version": 3,
  "background": {
    "service_worker": "background.js"
  }
}

While you can name the file that contains your background logic whatever you want, it can be helpful to use common conventions when discussing code samples in forums like this.

Is there no need for anything in the serviceWorker.js file? I would have thought it might be desired to have the serviceWorker.js message the content script depending on certain events.

Whether or not you need a background script depends on what you're trying to do. Extensions don't have to have a background context. For example, this sample bookmark extension only has a popup window that appears when the user clicks the extension's toolbar icon. Or, say you built an extension that restyles a website; your extension could be structured so all it does is inject a static CSS file onto pages that match specific URLs: no JavaScript needed at all! 

Generally speaking, it's best to avoid complexity until you need it. You can always refactor your code and move your static logic into an event handler if/when you need to. To that end, I'd recommend using statically declared content scripts rather than dynamically injecting a content script in response to tabs.onUpdated events.

Simeon - @dotproto


Cuyler Stuwe

unread,
May 23, 2023, 1:15:28 AM5/23/23
to Chromium Extensions, Alex Bishka, Cuyler Stuwe, Chromium Extensions
Yeah, even if it's written with frontend web tech, there's definitely a different set of skills needed for extension devs compared to the skills needed by CRUD app frontend devs.

There's some extra code in the project I handed you, because it's built from a template that I used to start some of my freelance Chrome Extension projects from back around the time MV3 was originally released. I wouldn't get too lost trying to read into what everything does. Probably 80-95% of what's in that template is not pertinent to the example.

Q&A:

Q: "Is there no need for anything in the serviceWorker.js file?"
A: It's just along for the ride as part of the template in my example.

Q: Is the serviceWorker.js file considered better practice than background.js?
A: The "background.js" file you're talking about is just an arbitrarily-named Javascript file defined as the "service worker" within manifest.json. You could call it "coffee.js" and as long as that's what you tell the manifest file says you're looking for as a service worker, it will work. The tutorial video you referenced seems to have called this file "background.js", which smells a little off to me since it's definitely different from the background script we used to have in MV2 extensions. To each their own, I suppose.

Q: "Why isn't anything within content.js wrapped around a chrome api call, like chrome.runtime.onMessage.addListener?"
A: You don't need that for content scripts. With a handful of caveats, you can generally think of a content script as "just a script running on the page". You could theoretically have different content scripts match different pages. In practice, most commercial browser extensions err on the side of having one content script that acts as the sole entry point. Maybe two if they want to accommodate a special case (e.g., for communication/DOM manipulation when their extension runs on their own site).

Q: "the tutorial I was following on YouTube"
A: I skimmed this. It feels like a pretty awkward patchwork mix of Javascript from different eras and traditions. Wrapping the entire content script in an IIFE feels very much straight out of 2010. It's pointless here since the whole purpose of that technique was to avoid variable name collisions with other scripts, but content scripts run in an "isolated world" by default where these collisions wouldn't happen anyway (not to mention that the "var" keyword isn't really in widespread use anymore). Functions like getElementsByClassName and getElementById are usually not preferred in extensions compared to querySelector and querySelectorAll, as the latter two are much more expressive and powerful. Generally the only time you'd prefer the former two is when you want to squeeze every last drop of performance out of a simple element query that must be called frequently, e.g., in response to page mutations. Listening to him talk about chrome.storage.sync, it wasn't clear to me that he was aware of its tiny storage quota and that chrome.storage.local exists as the "standard" storage area. All-in-all, it felt like some regular dev who read some docs and maybe wrote 1 extension is now teaching other devs how to do it. Probably not unprecedented for freeCodeCamp.

Q: "the GPT answers (although those were very meh) I got suggested that"
A: Yeah, ChatGPT is actually pretty overrated for learning new things. In order to write a prompt that's useful, you usually have to have lead it with some domain knowledge. But if you had that knowledge, you wouldn't have to ask. Catch-22. My bet is that others will eventually catch onto this as well (just as the world at large finally caught on that NFTs are essentially worthless), and this bubble of excess hype around AI will pop. There are some cool/useful use-cases, but it feels oversold right now.

Alex Bishka

unread,
May 23, 2023, 1:25:15 AM5/23/23
to Chromium Extensions, Cuyler Stuwe, Alex Bishka, Chromium Extensions
Thank you both for the responses, this has helped me a lot! Excited to learn even more as I explore and delve more into this project in the coming days.
Reply all
Reply to author
Forward
0 new messages