[2.0 rc.14 / next] SSR: a rejection contained by `<Errored>` is still an unhandled rejection when an async sibling shares its children slot (exits Node)
Los mantenedores suelen responder en 1 día
Nadie ha tomado este issue todavía.
Evaluación
- Dificultad
- 2/5
- Tiempo estimado
- 1-3 horas
- Aptitud para principiantes
- 66/100
- Tipo de issue
- Error
- Claridad
- Bien especificado
- Estado de actividad
- Activo
- Stack tecnológico
- nodejs, typescript
- Área
- frontend
Línea de trabajo
El error se lanza en createErrorBoundary en packages/solid/src/server/signals.ts, donde se crea el Promise.all(pending.p) combinado y se lanza como NotReadyError. Empieza por ahí y comprueba por qué nada espera esa promesa una vez que el elemento hermano mantiene el slot en pending. Ejecuta la reproducción del issue, que debería mostrar Error: boom como rechazo no gestionado. Está terminado cuando no se reporta ningún rechazo no gestionado y la salida HTML sigue igual, con el fallback Errored todavía renderizado.
Escrito por el modelo de indexación a partir del texto del issue.
Descripción
Summary
During renderToStream, an async read that rejects under <Errored> is contained (the fallback renders), but the rejection is also left unhandled when the boundary shares a {props.children} slot with an async sibling. Node's default --unhandled-rejections=throw then ends the process. Another <Errored> around the whole tree does not help: the error is not escaping the render, it is an internal promise nobody handles.
Reproduction
import { renderToStream } from "@solidjs/web";
import { createMemo, Errored, Loading } from "solid-js";
import type { ParentProps } from "solid-js";
const delay = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms));
process.on("unhandledRejection", (reason) => console.log("unhandled:", String(reason)));
function Layout(props: ParentProps) {
return <main>{props.children}</main>;
}
function Fails() {
const value = createMemo(async () => {
await delay(5);
throw new Error("boom");
});
return <p>{value()}</p>;
}
function Later() {
const value = createMemo(async () => {
await delay(20);
return "later";
});
return <p>{value()}</p>;
}
renderToStream(() => (
<Loading fallback={<p>loading</p>}>
<Layout>
<Errored fallback={(_error) => <p>failed</p>}>
<Fails />
</Errored>
<Later />
</Layout>
</Loading>
)).then((html) => console.log(html));
| HTML | unhandledRejection |
|
|---|---|---|
| as above | <main><p>failed</p><p>later</p></main> |
Error: boom |
wrapped in another <Errored> |
same | Error: boom |
<Later /> removed |
<main><p>failed</p></main> |
none |
<Later /> after </Layout> |
<main><p>failed</p></main><p>later</p> |
none |
no <Layout> (both directly in <Loading>) |
<p>failed</p><p>later</p> |
none |
Without the handler, Node exits. Reproduces on 2.0.0-rc.14, also with #3936 applied; the code below is unchanged on next.
Cause
The server boundary's resolve (packages/solid/src/server/signals.ts, createErrorBoundary) throws a combined promise while its holes are pending:
const all: any = Promise.all(pending.p);
if (pending.p.some(p => (p as any).$clientHole)) all.$clientHole = true;
throw new NotReadyError(all);
When the sibling keeps the slot pending and the slot is retried, nothing awaits that all any more, so its rejection is reported even though the boundary handled the error and rendered its fallback.
Expected
A rejection that <Errored> contains is handled, as for a rejecting direct child of <Loading> (#3570, #3594) and in the browser (#3769).
Possible fix
Mark the combined promise handled where it is created; whoever awaits it still sees the rejection:
const all: any = Promise.all(pending.p);
all.catch(() => {});
With that line in rc.14's dist/server.dev.js, the reproduction reports no unhandled rejection and renders the same HTML.
(submitted by Claude Opus 5.5 on behalf of rvlzzr)
- Lenguaje dominante
- TypeScript
- Estrellas
- 36.1k
- Forks
- 1.1k
- Merge medio
- 9 h 56 min
- PR fusionados (30 d)
- 336
Preparar el entorno
- Sin Dockerfile ni archivo de Docker Compose
- Tiene una plantilla de pull request
- Leer la guía de contribución
Primeros pasos
- Lee el issue completo y luego la guía de contribución del proyecto.
- Comenta en el issue que vas a ocuparte — evita que dos personas hagan lo mismo.
- Haz un fork del repositorio y trabaja en una rama.
- Abre un pull request que haga referencia al número del issue.
Más de solidjs/solid
-
Dificultad 2/5 1-3 horas Aptitud para principiantes 72/100
Los mantenedores suelen responder en 1 día
-
Dificultad 5/5 Más de una semana Aptitud para principiantes 12/100
solidjs/solid#3975 · 1 comentario ·
Los mantenedores suelen responder en 1 día
-
Dificultad 3/5 Medio día Aptitud para principiantes 32/100
solidjs/solid#3974 · 1 comentario ·
Los mantenedores suelen responder en 1 día
-
Dificultad 3/5 1-2 días Aptitud para principiantes 52/100
Los mantenedores suelen responder en 1 día
-
hydrate() run mid-parse leaves dead server nodes beside fresh client copies (async entry script)Abierto
Dificultad 4/5 3-5 días Aptitud para principiantes 20/100
Los mantenedores suelen responder en 1 día
Todos los issues de solidjs/solid
Issues similares
-
cleanName keeps a state code that's joined to the word before it (SavannahGa → "Savannah Ga")Abierto
Dificultad 2/5 1-3 horas Aptitud para principiantes 82/100
openwatersio/slackwater-database#281 ·
Los mantenedores suelen responder en 1 día
-
accepted bug via-triage
Dificultad 2/5 1-3 horas Aptitud para principiantes 76/100
pingdotgg/t3code#18690 · 1 comentario ·
Los mantenedores suelen responder en 1 día
-
Bayern feature:Application ready web
Dificultad 2/5 1-3 horas Aptitud para principiantes 67/100
digitalfabrik/entitlementcard#3176 ·
Los mantenedores suelen responder en 7 días
-
[BUG] 运行位置的分支列表不自动刷新,外部新建的分支看不到Abierto
Dificultad 2/5 1-3 horas Aptitud para principiantes 82/100
NanmiCoder/cc-haha#1533 · 1 comentario ·
Los mantenedores suelen responder en 1 día
-
Dificultad 2/5 1-3 horas Aptitud para principiantes 76/100
Los mantenedores suelen responder en 1 día