[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
- 10 h 44 min
- PR fusionados (30 d)
- 339
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 4/5 3-5 días Aptitud para principiantes 50/100
Los mantenedores suelen responder en 1 día
-
Dificultad 4/5 3-5 días Aptitud para principiantes 28/100
Los mantenedores suelen responder en 1 día
-
Dificultad 4/5 3-5 días Aptitud para principiantes 45/100
Los mantenedores suelen responder en 1 día
-
Dificultad 3/5 1-2 días Aptitud para principiantes 58/100
Los mantenedores suelen responder en 1 día
-
Dificultad 5/5 Más de una semana Aptitud para principiantes 35/100
Los mantenedores suelen responder en 1 día
Todos los issues de solidjs/solid
Issues similares
-
Dificultad 2/5 1-3 horas Aptitud para principiantes 70/100
NaturalIntelligence/fast-xml-parser#890 · 1 comentario ·
Los mantenedores suelen responder en 3 días
-
Dificultad 2/5 1-3 horas Aptitud para principiantes 62/100
zsviczian/obsidian-excalidraw-plugin#3000 ·
Los mantenedores suelen responder en 1 día
-
Dificultad 2/5 1-3 horas Aptitud para principiantes 68/100
pnpm/get.pnpm.io#68 ·
-
[BUG] Cloudflare KV binding 模式下 kvDriver.list() 沿用 EdgeOne 语义,字段名不匹配导致 key 格式下初始化 500 并耗尽 list 配额Posiblemente ocupada @wumingzhinu la tomó hoy. Abiertobug
Dificultad 2/5 1-3 horas Aptitud para principiantes 64/100
OpenListTeam/OpenList-Worker#125 ·
Los mantenedores suelen responder en 2 días
-
Android (vision-camera-location): HybridLocationManager never quits its HandlerThread on disposeAbierto
Dificultad 2/5 1-3 horas Aptitud para principiantes 74/100
margelo/react-native-vision-camera#4217 ·
Los mantenedores suelen responder en 4 días