Skip to content

$inspect should cause state_unsafe_mutation when setting $state inside it #16156

@timephy

Description

@timephy

Describe the bug

I found big inconsistencies in when/if the state_unsafe_mutation warning/error is shown.

This is the setup:

<script>
    class State {
        _x = $state(null)

        get_x() {
            if (this._x === null) {
                this._x = 5
            }
            return this._x
        }

        get x() {
            if (this._x === null) {
                this._x = 5
            }
            return this._x
        }
    }

    const s = new State()

    // $inspect(s.get_x()) // makes a difference when uncommented
    // $inspect(s.x) // makes a difference when uncommented
    // $inspect(s._x) // does NOT make a difference when uncommented
</script>

<h1>Hello {s.get_x()}!</h1>
<h1>Hello {s.x}!</h1>

This results in the following error:

state_unsafe_mutation Updating state inside a derived or a template expression is forbidden. If the value should not be reactive, declare it without `$state` https://svelte.dev/e/state_unsafe_mutation in {expression} in App.svelte

However, if one of the top two $inspects is uncommented, the code passes and runs with the expected result.
If the bottom $inspect is uncommented, the code fails and does not run, the above error is thrown.

Reproduction

https://svelte.dev/playground/686feb80ab0e41d29668232eacfdf8a4?version=5.34.2

Logs

System Info

playground

Severity

annoyance

Activity

paoloricciuti

paoloricciuti commented on Jun 14, 2025

@paoloricciuti
Member

This seems perfectly reasonable to me: inspect is invoked immediately which means the assignment doesn't happen in the template...if you access _x you are not writing to state so _x is still null when you get to the template and it's written to.

timephy

timephy commented on Jun 14, 2025

@timephy
Author

I also have a related question: Is what I describe below "allowed"?

<script>
	class State {
		_x = $state("loading...")

		get_x() {
			if (this._x === null) {
				loadDataAsync().then(() => (this._x = "loaded"))
			}
			return this._x
		}
	}

	const s = new State()

	// $inspect(s.get_x()) // makes a difference when uncommented
	// $inspect(s.x) // makes a difference when uncommented
	// $inspect(s._x) // does NOT make a difference when uncommented
</script>

<h1>Hello {s.get_x()}!</h1>
<h1>Hello {s.x}!</h1>

I got the error of this issue, when loadDataAsync() was cached and therefore ran the .then(...) function, setting the $state IMMEDIATELY.

To solve for this I used queueMicrotask(...)queueMicrotask(() => loadDataAsync().then(() => (this._x = "loaded"))).

So the eventual question is:
Can one safely use queueMicrotask inside a $derived or template?

timephy

timephy commented on Jun 14, 2025

@timephy
Author

This seems perfectly reasonable to me: inspect is invoked immediately which means the assignment doesn't happen in the template...if you access _x you are not writing to state so _x is still null when you get to the template and it's written to.

It does make sense! I agree.
But should it be like that?
imo it is misleading for $inspect to have "side-effects"...

Especially when $inspect(s.x) has an influence on whether {s.get_x()} errors or not!

changed the title [-]❗ Big inconsistencies with `state_unsafe_mutation`[/-] [+]❗Inconsistencies of using `$inspect` resulting in `state_unsafe_mutation`[/+] on Jun 14, 2025
paoloricciuti

paoloricciuti commented on Jun 14, 2025

@paoloricciuti
Member

imo it is misleading for $inspect to have "side-effects"...

Oh yeah it should not have side effects but can we prevent that? Maybe we should error out if you set state in inspect too? 🤔

timephy

timephy commented on Jun 14, 2025

@timephy
Author

I feel like that would make sense...

Should it make a difference from where you "observe"/"subscribe"? I guess not?

timephy

timephy commented on Jun 14, 2025

@timephy
Author

Can one safely use queueMicrotask inside a $derived or template?

So what about this? Is this allowed?

paoloricciuti

paoloricciuti commented on Jun 14, 2025

@paoloricciuti
Member

Can one safely use queueMicrotask inside a $derived or template?

It should be safe as in that case you are not really setting state in the derived

changed the title [-]❗Inconsistencies of using `$inspect` resulting in `state_unsafe_mutation`[/-] [+]`$inspect` should cause `state_unsafe_mutation` when setting `$state` inside it[/+] on Jun 14, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

      Development

      Participants

      @paoloricciuti@timephy

      Issue actions

        `$inspect` should cause `state_unsafe_mutation` when setting `$state` inside it · Issue #16156 · sveltejs/svelte