emberjs/ember.js · error · Error
Rehydration with nextSibling not supported
Error message
Rehydration with nextSibling not supported
What it means
The rehydration builder can only replay server-rendered HTML by walking the existing children of the parent element; it has no support for starting mid-stream at a specific sibling. The RehydrateBuilder constructor throws immediately if a non-null nextSibling cursor is supplied.
Source
Thrown at packages/@glimmer/runtime/lib/vm/rehydrate-builder.ts:51
constructor(
element: SimpleElement,
nextSibling: Nullable<SimpleNode>,
public readonly startingBlockDepth: number
) {
super(element, nextSibling);
this.openBlockDepth = startingBlockDepth - 1;
}
}
export class RehydrateTree extends NewTreeBuilder implements TreeBuilder {
private unmatchedAttributes: Nullable<SimpleAttr[]> = null;
declare cursors: Stack<RehydratingCursor>; // Hides property on base class
blockDepth = 0;
startingBlockOffset: number;
constructor(env: Environment, parentNode: SimpleElement, nextSibling: Nullable<SimpleNode>) {
super(env, parentNode, nextSibling);
if (nextSibling) throw new Error('Rehydration with nextSibling not supported');
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion -- @fixme
let node = this.currentCursor!.element.firstChild;
while (node !== null) {
if (isOpenBlock(node)) {
break;
}
node = node.nextSibling;
}
assert(node, 'Must have opening comment for rehydration.');
this.candidate = node;
const startingBlockOffset = getBlockDepth(node);
if (startingBlockOffset !== 0) {
// We are rehydrating from a partial tree and not the root component
// We need to add an extra block before the first block to rehydrate correctly
// The extra block is needed since the renderComponent API creates a synthetic component invocation which generates the extra blockView on GitHub (pinned to 26f97246a8)
Solutions
- Pass `null` as nextSibling when creating the rehydrating builder.
- If you must append relative to a sibling, clear or restructure the parent element so rehydration starts from the first child.
- Use the non-rehydrating element builder for insertion at a specific sibling position instead of RehydrateBuilder.
Example fix
// before let builder = new RehydrateBuilder(env, rootElement, anchorNode); // after let builder = new RehydrateBuilder(env, rootElement, null);
Defensive patterns
Strategy: validation
Validate before calling
if (nextSibling != null) {
throw new Error('RehydrateBuilder requires nextSibling === null');
}
const builder = new RehydrateBuilder(env, parentNode, null); Type guard
function canRehydrate(cursor) {
return cursor.nextSibling == null;
} Prevention
- Always pass null as nextSibling to RehydrateBuilder.
- Use the regular element builder when you need sibling-anchored insertion.
- Document rehydration constraints in any custom SSR host integration.
- Add an early assertion in your bootstrapping code around builder construction.
When it happens
Trigger: Constructing `new RehydrateBuilder(env, parentNode, nextSibling)` with a non-null nextSibling — i.e. attempting to serialize/rehydrate into a cursor that points at an insertion point between existing nodes.
Common situations: Embedding Glimmer SSR/rehydration into a custom host framework that uses element cursors with siblings; using Glimmer's client-side builder APIs against a rehydrating element with an anchor node.
Related errors
- @glimmer/runtime needs a valid "globalThis.URL"
- you must pass document or appendOperations to a new runtime
- Compile Error: ${template.problem} @ ${template.span.start}.
- A resolved helper cannot be passed as a named argument as th
- deprecation override for ${id} not found
AI-assisted analysis of emberjs/ember.js@26f97246a8 (2026-09-01).
Data as JSON: /api/errors/44d22c51eacc6a12.
Report an issue: GitHub.