Repository: pyscript/pyscript (language: python)

Issue:
Can't disable stderr DOM redirection
### Checklist

- [X] I added a descriptive title
- [X] I searched for other issues and couldn't find a solution or duplication
- [X] I already searched in Google and didn't find any good information or help

### What happened?

In the version of PyScript served by `latest/pyscript.net`, both stdout and stderr content is sent to the Javascript console.

In `releases/2024.10.1/core.js`, stdout and stderr is sent to the Javascript console - but stderr content is *also* sent to the DOM as red text in a pink box. 

This appears to be the expected default behaviour; however, there doesn't appear to be a way to turn this behavior *off*. I've seen several references to an `error` plugin in [older versions of the docs](https://docs.pyscript.net/2024.4.1/user-guide/configuration/#plugins), along with instructions for how to disable that plugin; but the 2024.10.1 docs don't seem to mention this plugin or disabling it. It's not even clear if the `plugins` configuration item is even supported any more - the [plugins page](https://docs.pyscript.net/2024.10.1/user-guide/plugins/) doesn't make any mention of the `pyscript.toml` configuration item that previously existed.

Making matters worse; the default stderr error handling on Micropython doesn't output text, but the ASCII values of any content printed to stderr, as one integer value per line (i.e., `print("This is an error", file=stderr)` generates a 16 DOM boxes containing "84", "104", "105", "115", "32", ...)

### What browsers are you seeing the problem on? (if applicable)

Chrome, Safari

### Console info

_No response_

### Additional Context

Reproduction case: https://gist.github.com/freakboy3742/ed3073fcffd6d92bb374bcf85301f320

I discovered this in the context of BeeWare; Toga invokes `warnings.warn()` for some unimplemented features, which outputs to stderr by default. Third party libraries also frequently raise warnings as part of a migration process. Having those warnings surface on the DOM is undesirable from our perspective.


Discussion / hints:
just FYI `py-config` doesn't pollute `mpy` environment/interpreter ... you need an `mpy-config` there due extremely different nature of the two environments (a reason for attribute `config` to be preferred in general).

I am not sure that final `mpy` script in your example is meant to ignore the config, and I've already fixed the underlying issue locally, but if that was the case, it should have a `config` explicit attribute too to confirm stuff works as expected in there too.

Fix patch:
diff --git a/core/package-lock.json b/core/package-lock.json
index 88970054599..eba5bdb3604 100644
--- a/core/package-lock.json
+++ b/core/package-lock.json
@@ -1,19 +1,19 @@
 {
     "name": "@pyscript/core",
-    "version": "0.6.6",
+    "version": "0.6.7",
     "lockfileVersion": 3,
     "requires": true,
     "packages": {
         "": {
             "name": "@pyscript/core",
-            "version": "0.6.6",
+            "version": "0.6.7",
             "license": "APACHE-2.0",
             "dependencies": {
                 "@ungap/with-resolvers": "^0.1.0",
                 "@webreflection/idb-map": "^0.3.2",
-                "add-promise-listener": "^0.1.1",
+                "add-promise-listener": "^0.1.3",
                 "basic-devtools": "^0.1.6",
-                "polyscript": "^0.16.2",
+                "polyscript": "^0.16.3",
                 "sabayon": "^0.5.2",
                 "sticky-module": "^0.1.1",
                 "to-json-callback": "^0.1.1",
@@ -26,7 +26,7 @@
                 "@codemirror/state": "^6.4.1",
                 "@codemirror/view": "^6.34.1",
                 "@playwright/test": "1.45.3",
-                "@rollup/plugin-commonjs": "^28.0.0",
+                "@rollup/plugin-commonjs": "^28.0.1",
                 "@rollup/plugin-node-resolve": "^15.3.0",
                 "@rollup/plugin-terser": "^0.4.4",
                 "@webreflection/toml-j0.4": "^1.1.3",
@@ -560,19 +560,19 @@
             }
         },
         "node_modules/@rollup/plugin-commonjs": {
-            "version": "28.0.0",
-            "resolved": "https://registry.npmjs.org/@rollup/plugin-commonjs/-/plugin-commonjs-28.0.0.tgz",
-            "integrity": "sha512-BJcu+a+Mpq476DMXG+hevgPSl56bkUoi88dKT8t3RyUp8kGuOh+2bU8Gs7zXDlu+fyZggnJ+iOBGrb/O1SorYg==",
+            "version": "28.0.1",
+            "resolved": "https://registry.npmjs.org/@rollup/plugin-commonjs/-/plugin-commonjs-28.0.1.tgz",
+            "integrity": "sha512-+tNWdlWKbpB3WgBN7ijjYkq9X5uhjmcvyjEght4NmH5fAU++zfQzAJ6wumLS+dNcvwEZhKx2Z+skY8m7v0wGSA==",
             "dev": true,
             "license": "MIT",
             "dependencies": {
                 "@rollup/pluginutils": "^5.0.1",
                 "commondir": "^1.0.1",
                 "estree-walker": "^2.0.2",
-                "fdir": "^6.1.1",
+                "fdir": "^6.2.0",
                 "is-reference": "1.2.1",
                 "magic-string": "^0.30.3",
-                "picomatch": "^2.3.1"
+                "picomatch": "^4.0.2"
             },
             "engines": {
                 "node": ">=16.0.0 || 14 >= 14.17"
@@ -586,19 +586,6 @@
                 }
             }
         },
-        "node_modules/@rollup/plugin-commonjs/node_modules/picomatch": {
-            "version": "2.3.1",
-            "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
-            "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
-            "dev": true,
-            "license": "MIT",
-            "engines": {
-                "node": ">=8.6"
-            },
-            "funding": {
-                "url": "https://github.com/sponsors/jonschlinkert"
-            }
-        },
         "node_modules/@rollup/plugin-node-resolve": {
             "version": "15.3.0",
             "resolved": "https://registry.npmjs.org/@rollup/plugin-node-resolve/-/plugin-node-resolve-15.3.0.tgz",
@@ -1020,9 +1007,9 @@
             }
         },
         "node_modules/add-promise-listener": {
-            "version": "0.1.1",
-            "resolved": "https://registry.npmjs.org/add-promise-listener/-/add-promise-listener-0.1.1.tgz",
-            "integrity": "sha512-b3DQJ4VBQ1e4bjVPd0mqHkgFt4MYD8jYTEcfN9Qx+bGYs+WRLxPDnX7fRztGRm1k4CZzeXWvvYif6L1q4TToqg==",
+            "version": "0.1.3",
+            "resolved": "https://registry.npmjs.org/add-promise-listener/-/add-promise-listener-0.1.3.tgz",
+            "integrity": "sha512-hQ6IgGJ7NvvlPYbwdekhdVwPb4QzEptNZ5v7B4XRKz7FukUPDuF/v+R5EFHArWmhmq4d+xv0G4/B5bu2GSiz9Q==",
             "license": "MIT"
         },
         "node_modules/ajv": {
@@ -2588,8 +2575,6 @@
             "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==",
             "dev": true,
             "license": "MIT",
-            "optional": true,
-            "peer": true,
             "engines": {
                 "node": ">=12"
             },
@@ -2649,9 +2634,9 @@
             }
         },
         "node_modules/polyscript": {
-            "version": "0.16.2",
-            "resolved": "https://registry.npmjs.org/polyscript/-/polyscript-0.16.2.tgz",
-            "integrity": "sha512-S+RHW3ogB4nFlajp+Pd+YWuoEJMRBNuFwcke3H/lxlq8ZkX2lEZhvvda+QcwAsQXitmWg3Wi0Oz8fuwXyIKZeg==",
+            "version": "0.16.3",
+            "resolved": "https://registry.npmjs.org/polyscript/-/polyscript-0.16.3.tgz",
+            "integrity": "sha512-I3kHxt62FMRAX2iVl24iCEtG4UnUInMSbv/LnwevkmjOErLPAQtES4CNzU/fgKRpXYCqp0WWQaRvRYkJhpMIbA==",
             "license": "APACHE-2.0",
             "dependencies": {
                 "@ungap/structured-clone": "^1.2.0",
diff --git a/core/package.json b/core/package.json
index 26c13526f32..3c3994d0131 100644
--- a/core/package.json
+++ b/core/package.json
@@ -1,6 +1,6 @@
 {
     "name": "@pyscript/core",
-    "version": "0.6.6",
+    "version": "0.6.7",
     "type": "module",
     "description": "PyScript",
     "module": "./index.js",
@@ -60,9 +60,9 @@
     "dependencies": {
         "@ungap/with-resolvers": "^0.1.0",
         "@webreflection/idb-map": "^0.3.2",
-        "add-promise-listener": "^0.1.1",
+        "add-promise-listener": "^0.1.3",
         "basic-devtools": "^0.1.6",
-        "polyscript": "^0.16.2",
+        "polyscript": "^0.16.3",
         "sabayon": "^0.5.2",
         "sticky-module": "^0.1.1",
         "to-json-callback": "^0.1.1",
@@ -75,7 +75,7 @@
         "@codemirror/state": "^6.4.1",
         "@codemirror/view": "^6.34.1",
         "@playwright/test": "1.45.3",
-        "@rollup/plugin-commonjs": "^28.0.0",
+        "@rollup/plugin-commonjs": "^28.0.1",
         "@rollup/plugin-node-resolve": "^15.3.0",
         "@rollup/plugin-terser": "^0.4.4",
         "@webreflection/toml-j0.4": "^1.1.3",
diff --git a/core/src/config.js b/core/src/config.js
index 040bbbe58c9..c6a32c8852c 100644
--- a/core/src/config.js
+++ b/core/src/config.js
@@ -146,6 +146,8 @@ for (const [TYPE] of TYPES) {
             }
         } else if (!parsed?.plugins?.includes(`!${key}`)) {
             toBeAwaited.push(value().then(({ default: p }) => p));
+        } else if (key === "error") {
+            toBeAwaited.push(value().then(({ notOnDOM }) => notOnDOM()));
         }
     }
 
diff --git a/core/src/plugins/error.js b/core/src/plugins/error.js
index 7eb3dde2b42..2ff353131ee 100644
--- a/core/src/plugins/error.js
+++ b/core/src/plugins/error.js
@@ -1,6 +1,12 @@
 // PyScript Error Plugin
+import { buffered } from "polyscript/exports";
 import { hooks } from "../core.js";
 
+let dontBotherDOM = false;
+export function notOnDOM() {
+    dontBotherDOM = true;
+}
+
 hooks.main.onReady.add(function override(pyScript) {
     // be sure this override happens only once
     hooks.main.onReady.delete(override);
@@ -8,13 +14,15 @@ hooks.main.onReady.add(function override(pyScript) {
     // trap generic `stderr` to propagate to it regardless
     const { stderr } = pyScript.io;
 
-    // override it with our own logic
-    pyScript.io.stderr = (error, ...rest) => {
+    const cb = (error, ...rest) => {
         notify(error.message || error);
         // let other plugins or stderr hook, if any, do the rest
         return stderr(error, ...rest);
     };
 
+    // override it with our own logic
+    pyScript.io.stderr = pyScript.type === "py" ? cb : buffered(cb);
+
     // be sure uncaught Python errors are also visible
     addEventListener("error", ({ message }) => {
         if (message.startsWith("Uncaught PythonError")) notify(message);
@@ -30,6 +38,7 @@ hooks.main.onReady.add(function override(pyScript) {
  * @param {string} message
  */
 export function notify(message) {
+    if (dontBotherDOM) return;
     const div = document.createElement("div");
     div.className = "py-error";
     div.textContent = message;
diff --git a/core/types/plugins/error.d.ts b/core/types/plugins/error.d.ts
index bf9a6788ab5..40bdfd0dc2d 100644
--- a/core/types/plugins/error.d.ts
+++ b/core/types/plugins/error.d.ts
@@ -1,3 +1,4 @@
+export function notOnDOM(): void;
 /**
  * Add a banner to the top of the page, notifying the user of an error
  * @param {string} message


Test patch:
diff --git a/core/tests/index.html b/core/tests/index.html
index 7a09479e770..43971239809 100644
--- a/core/tests/index.html
+++ b/core/tests/index.html
@@ -14,5 +14,5 @@
             a:hover { opacity: 1; }
         </style>
     </head>
-    <body><ul><li><strong><span>javascript</span></strong><ul><li><a href="./javascript/async-listener.html">async-listener<small>.html</small></a></li><li><a href="./javascript/config-url.html">config-url<small>.html</small></a></li><li><a href="./javascript/config_type.html">config_type<small>.html</small></a></li><li><strong><a href="./javascript/fetch/index.html">fetch</a></strong></li><li><a href="./javascript/ffi.html">ffi<small>.html</small></a></li><li><a href="./javascript/hooks.html">hooks<small>.html</small></a></li><li><strong><a href="./javascript/issue-2093/index.html">issue-2093</a></strong></li><li><a href="./javascript/js-storage.html">js-storage<small>.html</small></a></li><li><a href="./javascript/js_modules.html">js_modules<small>.html</small></a></li><li><strong><a href="./javascript/loader/index.html">loader</a></strong></li><li><a href="./javascript/mpy.html">mpy<small>.html</small></a></li><li><a href="./javascript/py-terminal-main.html">py-terminal-main<small>.html</small></a></li><li><a href="./javascript/py-terminal-worker.html">py-terminal-worker<small>.html</small></a></li><li><a href="./javascript/py-terminal.html">py-terminal<small>.html</small></a></li><li><a href="./javascript/py-terminals.html">py-terminals<small>.html</small></a></li><li><strong><a href="./javascript/pyodide-cache/index.html">pyodide-cache</a></strong></li><li><a href="./javascript/storage.html">storage<small>.html</small></a></li><li><strong><span>workers</span></strong><ul><li><strong><a href="./javascript/workers/create_named/index.html">create_named</a></strong></li><li><strong><a href="./javascript/workers/mpy/index.html">mpy</a></strong></li><li><strong><a href="./javascript/workers/py/index.html">py</a></strong></li></ul></li></ul></li><li><strong><a href="./manual/index.html">manual</a></strong><ul><li><a href="./manual/all-done.html">all-done<small>.html</small></a></li><li><a href="./manual/async.html">async<small>.html</small></a></li><li><a href="./manual/camera.html">camera<small>.html</small></a></li><li><a href="./manual/click.html">click<small>.html</small></a></li><li><a href="./manual/code-a-part.html">code-a-part<small>.html</small></a></li><li><a href="./manual/combo.html">combo<small>.html</small></a></li><li><a href="./manual/config.html">config<small>.html</small></a></li><li><a href="./manual/create-element.html">create-element<small>.html</small></a></li><li><a href="./manual/dialog.html">dialog<small>.html</small></a></li><li><a href="./manual/display.html">display<small>.html</small></a></li><li><strong><a href="./manual/donkey/index.html">donkey</a></strong></li><li><a href="./manual/error.html">error<small>.html</small></a></li><li><a href="./manual/html-decode.html">html-decode<small>.html</small></a></li><li><a href="./manual/input.html">input<small>.html</small></a></li><li><a href="./manual/interpreter.html">interpreter<small>.html</small></a></li><li><strong><a href="./manual/issue-7015/index.html">issue-7015</a></strong></li><li><a href="./manual/multi.html">multi<small>.html</small></a></li><li><a href="./manual/multiple-editors.html">multiple-editors<small>.html</small></a></li><li><a href="./manual/no-error.html">no-error<small>.html</small></a></li><li><strong><a href="./manual/no_sab/index.html">no_sab</a></strong></li><li><strong><a href="./manual/piratical/index.html">piratical</a></strong></li><li><a href="./manual/py-editor.html">py-editor<small>.html</small></a></li><li><a href="./manual/py-editor-failure.html">py-editor-failure<small>.html</small></a></li><li><strong><a href="./manual/py-terminals/index.html">py-terminals</a></strong><ul><li><a href="./manual/py-terminals/no-repl.html">no-repl<small>.html</small></a></li><li><a href="./manual/py-terminals/repl.html">repl<small>.html</small></a></li></ul></li><li><a href="./manual/py_modules.html">py_modules<small>.html</small></a></li><li><strong><a href="./manual/service-worker/index.html">service-worker</a></strong></li><li><a href="./manual/split-config.html">split-config<small>.html</small></a></li><li><a href="./manual/submit.html">submit<small>.html</small></a></li><li><a href="./manual/target.html">target<small>.html</small></a></li><li><a href="./manual/test_display_HTML.html">test_display_HTML<small>.html</small></a></li><li><a href="./manual/test_when.html">test_when<small>.html</small></a></li><li><a href="./manual/worker.html">worker<small>.html</small></a></li></ul></li><li><strong><a href="./python/index.html">python</a></strong></li></ul></body>
+    <body><ul><li><strong><span>javascript</span></strong><ul><li><a href="./javascript/async-listener.html">async-listener<small>.html</small></a></li><li><a href="./javascript/config-url.html">config-url<small>.html</small></a></li><li><a href="./javascript/config_type.html">config_type<small>.html</small></a></li><li><strong><a href="./javascript/fetch/index.html">fetch</a></strong></li><li><a href="./javascript/ffi.html">ffi<small>.html</small></a></li><li><a href="./javascript/hooks.html">hooks<small>.html</small></a></li><li><strong><a href="./javascript/issue-2093/index.html">issue-2093</a></strong></li><li><a href="./javascript/js-storage.html">js-storage<small>.html</small></a></li><li><a href="./javascript/js_modules.html">js_modules<small>.html</small></a></li><li><strong><a href="./javascript/loader/index.html">loader</a></strong></li><li><a href="./javascript/mpy-error.html">mpy-error<small>.html</small></a></li><li><a href="./javascript/mpy-no-error.html">mpy-no-error<small>.html</small></a></li><li><a href="./javascript/mpy.html">mpy<small>.html</small></a></li><li><a href="./javascript/py-terminal-main.html">py-terminal-main<small>.html</small></a></li><li><a href="./javascript/py-terminal-worker.html">py-terminal-worker<small>.html</small></a></li><li><a href="./javascript/py-terminal.html">py-terminal<small>.html</small></a></li><li><a href="./javascript/py-terminals.html">py-terminals<small>.html</small></a></li><li><strong><a href="./javascript/pyodide-cache/index.html">pyodide-cache</a></strong></li><li><a href="./javascript/storage.html">storage<small>.html</small></a></li><li><strong><span>workers</span></strong><ul><li><strong><a href="./javascript/workers/create_named/index.html">create_named</a></strong></li><li><strong><a href="./javascript/workers/mpy/index.html">mpy</a></strong></li><li><strong><a href="./javascript/workers/py/index.html">py</a></strong></li></ul></li></ul></li><li><strong><a href="./manual/index.html">manual</a></strong><ul><li><a href="./manual/all-done.html">all-done<small>.html</small></a></li><li><a href="./manual/async.html">async<small>.html</small></a></li><li><a href="./manual/camera.html">camera<small>.html</small></a></li><li><a href="./manual/click.html">click<small>.html</small></a></li><li><a href="./manual/code-a-part.html">code-a-part<small>.html</small></a></li><li><a href="./manual/combo.html">combo<small>.html</small></a></li><li><a href="./manual/config.html">config<small>.html</small></a></li><li><a href="./manual/create-element.html">create-element<small>.html</small></a></li><li><a href="./manual/dialog.html">dialog<small>.html</small></a></li><li><a href="./manual/display.html">display<small>.html</small></a></li><li><strong><a href="./manual/donkey/index.html">donkey</a></strong></li><li><a href="./manual/error.html">error<small>.html</small></a></li><li><a href="./manual/html-decode.html">html-decode<small>.html</small></a></li><li><a href="./manual/input.html">input<small>.html</small></a></li><li><a href="./manual/interpreter.html">interpreter<small>.html</small></a></li><li><strong><a href="./manual/issue-7015/index.html">issue-7015</a></strong></li><li><a href="./manual/multi.html">multi<small>.html</small></a></li><li><a href="./manual/multiple-editors.html">multiple-editors<small>.html</small></a></li><li><a href="./manual/no-error.html">no-error<small>.html</small></a></li><li><strong><a href="./manual/no_sab/index.html">no_sab</a></strong></li><li><strong><a href="./manual/piratical/index.html">piratical</a></strong></li><li><a href="./manual/py-editor.html">py-editor<small>.html</small></a></li><li><a href="./manual/py-editor-failure.html">py-editor-failure<small>.html</small></a></li><li><strong><a href="./manual/py-terminals/index.html">py-terminals</a></strong><ul><li><a href="./manual/py-terminals/no-repl.html">no-repl<small>.html</small></a></li><li><a href="./manual/py-terminals/repl.html">repl<small>.html</small></a></li></ul></li><li><a href="./manual/py_modules.html">py_modules<small>.html</small></a></li><li><strong><a href="./manual/service-worker/index.html">service-worker</a></strong></li><li><a href="./manual/split-config.html">split-config<small>.html</small></a></li><li><a href="./manual/submit.html">submit<small>.html</small></a></li><li><a href="./manual/target.html">target<small>.html</small></a></li><li><a href="./manual/test_display_HTML.html">test_display_HTML<small>.html</small></a></li><li><a href="./manual/test_when.html">test_when<small>.html</small></a></li><li><a href="./manual/worker.html">worker<small>.html</small></a></li></ul></li><li><strong><a href="./python/index.html">python</a></strong></li></ul></body>
 </html>
diff --git a/core/tests/javascript/mpy-error.html b/core/tests/javascript/mpy-error.html
new file mode 100644
index 00000000000..a8c62206bec
--- /dev/null
+++ b/core/tests/javascript/mpy-error.html
@@ -0,0 +1,14 @@
+<!doctype html>
+<html lang="en">
+    <head>
+        <meta charset="UTF-8" />
+        <meta name="viewport" content="width=device-width, initial-scale=1.0" />
+        <script type="module" src="../../dist/core.js"></script>
+        <script type="mpy">
+            from pyscript import document
+            import sys
+            print("This is an error", file=sys.stderr)
+            document.documentElement.classList.add("ok");
+        </script>
+    </head>
+</html>
diff --git a/core/tests/javascript/mpy-no-error.html b/core/tests/javascript/mpy-no-error.html
new file mode 100644
index 00000000000..6084dcc2388
--- /dev/null
+++ b/core/tests/javascript/mpy-no-error.html
@@ -0,0 +1,14 @@
+<!doctype html>
+<html lang="en">
+    <head>
+        <meta charset="UTF-8" />
+        <meta name="viewport" content="width=device-width, initial-scale=1.0" />
+        <script type="module" src="../../dist/core.js"></script>
+        <script type="mpy" config="mpy-no-error.toml">
+            from pyscript import document
+            import sys
+            print("This is an error", file=sys.stderr)
+            document.documentElement.classList.add("ok");
+        </script>
+    </head>
+</html>
diff --git a/core/tests/javascript/mpy-no-error.toml b/core/tests/javascript/mpy-no-error.toml
new file mode 100644
index 00000000000..4e7f1f7e4d3
--- /dev/null
+++ b/core/tests/javascript/mpy-no-error.toml
@@ -0,0 +1,1 @@
+plugins = ["!error"]
diff --git a/core/tests/js_tests.spec.js b/core/tests/js_tests.spec.js
index aff590e01f4..0fa09b99502 100644
--- a/core/tests/js_tests.spec.js
+++ b/core/tests/js_tests.spec.js
@@ -138,3 +138,17 @@ test('Pyodide lockFileURL vs CDN', async ({ page }) => {
   const body = await page.evaluate(() => document.body.textContent);
   await expect(body).toBe('OK');
 });
+
+test('MicroPython buffered error', async ({ page }) => {
+  await page.goto('http://localhost:8080/tests/javascript/mpy-error.html');
+  await page.waitForSelector('html.ok');
+  const body = await page.evaluate(() => document.body.textContent.trim());
+  await expect(body).toBe('This is an error');
+});
+
+test('MicroPython buffered NO error', async ({ page }) => {
+  await page.goto('http://localhost:8080/tests/javascript/mpy-no-error.html');
+  await page.waitForSelector('html.ok');
+  const body = await page.evaluate(() => document.body.textContent.trim());
+  await expect(body).toBe('');
+});
diff --git a/core/tests/manual/error/index.html b/core/tests/manual/error/index.html
new file mode 100644
index 00000000000..04b50f104b2
--- /dev/null
+++ b/core/tests/manual/error/index.html
@@ -0,0 +1,39 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+  <meta charset="UTF-8">
+  <meta name="viewport" content="width=device-width, initial-scale=1.0">
+  <title>PyScript Error Bug?</title>
+  <link rel="stylesheet" href="../../../dist/core.css">
+  <script type="module" src="../../../dist/core.js"></script>
+</head>
+<body>
+  <py-config>
+      plugins = ["!error"]
+  </py-config>
+  <script type="py">
+    import sys
+    print("This is normal content")
+    print("This is error content", file=sys.stderr)
+  </script>
+  <!-- Attempt 2; inline config
+  <script type="py" config='plugins=["!error"]'>
+    import sys
+    print("This is normal content")
+    print("This is error content", file=sys.stderr)
+  </script> -->
+  <!-- Attempt 3; external pyscript.toml
+  <script type="py" config="pyscript.toml">
+    import sys
+    print("This is normal content")
+    print("This is error content", file=sys.stderr)
+  </script> -->
+  <!-- Attempt 4; micropython
+  <script type="mpy">
+    import sys
+    print("This is normal content")
+    print("This is error content", file=sys.stderr)
+  </script> -->
+  <div id="result"></div>
+</body>
+</html>
diff --git a/core/tests/manual/error/pyscript.toml b/core/tests/manual/error/pyscript.toml
new file mode 100644
index 00000000000..4e7f1f7e4d3
--- /dev/null
+++ b/core/tests/manual/error/pyscript.toml
@@ -0,0 +1,1 @@
+plugins = ["!error"]