# Buggy code
def wrap_func(func):
    @wraps(func)
    def add_context(*args, **kwargs):
        # don't touch the comment on the next line - used by debugger
        output_value = func(*args, **kwargs)  # %% callback invoked %%
        if multi:
            if not isinstance(output_value, (list, tuple)):
                raise exceptions.InvalidCallbackReturnValue(
                    "The callback {} is a multi-output.\n"
                    "Expected the output type to be a list"
                    " or tuple but got {}.".format(callback_id, repr(output_value))
                )

            if not len(output_value) == len(output):
                raise exceptions.InvalidCallbackReturnValue(
                    "Invalid number of output values for {}.\n"
                    " Expected {} got {}".format(
                        callback_id, len(output), len(output_value)
                    )
                )

            component_ids = collections.defaultdict(dict)
            has_update = False
            for i, o in enumerate(output):
                val = output_value[i]
                if val is not no_update:
                    has_update = True
                    o_id, o_prop = o.component_id, o.component_property
                    component_ids[o_id][o_prop] = val

            if not has_update:
                raise exceptions.PreventUpdate

            response = {"response": component_ids, "multi": True}
        else:
            if output_value is no_update:
                raise exceptions.PreventUpdate

            response = {
                "response": {"props": {output.component_property: output_value}}
            }

        try:
            jsonResponse = json.dumps(response, cls=plotly.utils.PlotlyJSONEncoder)
        except TypeError:
            self._validate_callback_output(output_value, output)
            raise exceptions.InvalidCallbackReturnValue(
                dedent(
                    """
                    The callback for property `{property:s}`
                    of component `{id:s}` returned a value
                    which is not JSON serializable.

                    In general, Dash properties can only be
                    dash components, strings, dictionaries, numbers, None,
                    or lists of those.
                    """
                ).format(
                    property=output.component_property,
                    id=output.component_id,
                )
            )

        return jsonResponse

    self.callback_map[callback_id]["callback"] = add_context

    return add_context


# Traceback (TypeError)
Exception on /_dash-update-component [POST]
Traceback (most recent call last):
File "/home/kz/Envs/dash_bleedingedge/lib/python3.7/site-packages/dash/dash.py", line 1328, in add_context
response, cls=plotly.utils.PlotlyJSONEncoder
File "/home/kz/.local/pyenv/versions/3.7.3/lib/python3.7/json/__init__.py", line 238, in dumps
**kw).encode(obj)
File "/home/kz/Envs/dash_bleedingedge/lib/python3.7/site-packages/_plotly_utils/utils.py", line 44, in encode
encoded_o = super(PlotlyJSONEncoder, self).encode(o)
File "/home/kz/.local/pyenv/versions/3.7.3/lib/python3.7/json/encoder.py", line 199, in encode
chunks = self.iterencode(o, _one_shot=True)
File "/home/kz/.local/pyenv/versions/3.7.3/lib/python3.7/json/encoder.py", line 257, in iterencode
return _iterencode(o, 0)
File "/home/kz/Envs/dash_bleedingedge/lib/python3.7/site-packages/_plotly_utils/utils.py", line 113, in default
return _json.JSONEncoder.default(self, obj)
File "/home/kz/.local/pyenv/versions/3.7.3/lib/python3.7/json/encoder.py", line 179, in default
raise TypeError(f'Object of type {o.__class__.__name__} '
TypeError: Object of type _NoUpdate is not JSON serializable

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
File "/home/kz/Envs/dash_bleedingedge/lib/python3.7/site-packages/flask/app.py", line 2446, in wsgi_app
response = self.full_dispatch_request()
File "/home/kz/Envs/dash_bleedingedge/lib/python3.7/site-packages/flask/app.py", line 1951, in full_dispatch_request
rv = self.handle_user_exception(e)
File "/home/kz/Envs/dash_bleedingedge/lib/python3.7/site-packages/flask/app.py", line 1820, in handle_user_exception
reraise(exc_type, exc_value, tb)
File "/home/kz/Envs/dash_bleedingedge/lib/python3.7/site-packages/flask/_compat.py", line 39, in reraise
raise value
File "/home/kz/Envs/dash_bleedingedge/lib/python3.7/site-packages/flask/app.py", line 1949, in full_dispatch_request
rv = self.dispatch_request()
File "/home/kz/Envs/dash_bleedingedge/lib/python3.7/site-packages/flask/app.py", line 1935, in dispatch_request
return self.view_functions[rule.endpoint](**req.view_args)
File "/home/kz/Envs/dash_bleedingedge/lib/python3.7/site-packages/dash/dash.py", line 1404, in dispatch
response.set_data(self.callback_map[output]["callback"](*args))
File "/home/kz/Envs/dash_bleedingedge/lib/python3.7/site-packages/dash/dash.py", line 1331, in add_context
self._validate_callback_output(output_value, output)
File "/home/kz/Envs/dash_bleedingedge/lib/python3.7/site-packages/dash/dash.py", line 1187, in _validate_callback_output
_validate_value(output_value)
File "/home/kz/Envs/dash_bleedingedge/lib/python3.7/site-packages/dash/dash.py", line 1180, in _validate_value
toplevel=True,
File "/home/kz/Envs/dash_bleedingedge/lib/python3.7/site-packages/dash/dash.py", line 1125, in _raise_invalid
bad_val=bad_val,
dash.exceptions.InvalidCallbackReturnValue:
The callback for `<Output `approot.children`>`
returned a value having type `_NoUpdate`
which is not JSON serializable.

The value in question is either the only value returned,
or is in the top level of the returned list,
and has string representation
`<dash.dash._NoUpdate object at 0x7ff2ca37ccf8>`

In general, Dash properties can only be
dash components, strings, dictionaries, numbers, None,
or lists of those.

# Fixed code
def wrap_func(func):
    @wraps(func)
    def add_context(*args, **kwargs):
        # don't touch the comment on the next line - used by debugger
        output_value = func(*args, **kwargs)  # %% callback invoked %%
        if multi:
            if not isinstance(output_value, (list, tuple)):
                raise exceptions.InvalidCallbackReturnValue(
                    "The callback {} is a multi-output.\n"
                    "Expected the output type to be a list"
                    " or tuple but got {}.".format(callback_id, repr(output_value))
                )

            if not len(output_value) == len(output):
                raise exceptions.InvalidCallbackReturnValue(
                    "Invalid number of output values for {}.\n"
                    " Expected {} got {}".format(
                        callback_id, len(output), len(output_value)
                    )
                )

            component_ids = collections.defaultdict(dict)
            has_update = False
            for i, o in enumerate(output):
                val = output_value[i]
                if not isinstance(val, _NoUpdate):
                    has_update = True
                    o_id, o_prop = o.component_id, o.component_property
                    component_ids[o_id][o_prop] = val

            if not has_update:
                raise exceptions.PreventUpdate

            response = {"response": component_ids, "multi": True}
        else:
            if isinstance(output_value, _NoUpdate):
                raise exceptions.PreventUpdate

            response = {
                "response": {"props": {output.component_property: output_value}}
            }

        try:
            jsonResponse = json.dumps(response, cls=plotly.utils.PlotlyJSONEncoder)
        except TypeError:
            self._validate_callback_output(output_value, output)
            raise exceptions.InvalidCallbackReturnValue(
                dedent(
                    """
                    The callback for property `{property:s}`
                    of component `{id:s}` returned a value
                    which is not JSON serializable.

                    In general, Dash properties can only be
                    dash components, strings, dictionaries, numbers, None,
                    or lists of those.
                    """
                ).format(
                    property=output.component_property,
                    id=output.component_id,
                )
            )

        return jsonResponse

    self.callback_map[callback_id]["callback"] = add_context

    return add_context