svelte icon indicating copy to clipboard operation
svelte copied to clipboard

Accessing an async derived in a derived declared before the async derived throws an un-descriptive error

Open paoloricciuti opened this issue 4 months ago • 1 comments

Describe the bug

This code

<script>
	function get(){
		return b;
	}
	const a = $derived(get());
	const b = $derived(await 42);
</script>

{a}

throws a Cannot read properties of undefined (reading 'f') in <unknown> in __wrapper.svelte

because when it tries to get b since b it's an async derived it's not initialized yet by the time the function for a executes. We should at least throw a more descriptive error (or possibly fix this).

Reproduction

REPL

Logs


System Info

repl

Severity

annoyance

paoloricciuti avatar Nov 26 '25 18:11 paoloricciuti

This is caused by out-of-order rendering:

right now, if you write a component like this

<script>
    function get(){
        return b;
    }

    const b = $derived(await 42);
</script>

{get()}

it is converted to this

import 'svelte/internal/disclose-version';
import 'svelte/internal/flags/async';
import * as $ from 'svelte/internal/client';

export default function App($$anchor) {
    function get() {
        return $.get(b);
    }

    var b;
    var $$promises = $.run([async () => b = await $.async_derived(() => 42)]);

    $.next();

    var text = $.text();

    $.template_effect(($0) => $.set_text(text, $0), [get], void 0, [$$promises[0]]);
    $.append($$anchor, text);
}

correctly identifying that get has $$promises[0] as a blocker, but if I put get() in a derived

<script>
    function get(){
        return b;
    }
    const a = $derived(get());
    const b = $derived(await 42);
</script>

{a}

it gets converted to this

import 'svelte/internal/disclose-version';
import 'svelte/internal/flags/async';
import * as $ from 'svelte/internal/client';

export default function App($$anchor) {
    function get() {
        return $.get(b);
    }

    const a = $.derived(get);
    var b;
    var $$promises = $.run([async () => b = await $.async_derived(() => 42)]);

    $.next();

    var text = $.text();

    $.template_effect(() => $.set_text(text, $.get(a)));
    $.append($$anchor, text);
}

And the blocker is not there, leading to a runtime error.

Trying to fix this but I dunno if I will succeed :D

paoloricciuti avatar Nov 26 '25 22:11 paoloricciuti