Repository: plotly/dash
File: dash/_callback.py (Python)

Code before:
                    callback_manager.terminate_job(job_id)

                if multi and isinstance(output_value, (list, tuple)):
                    output_value = [
                        NoUpdate() if NoUpdate.is_no_update(r) else r
                        for r in output_value
                    ]
                updated_props = callback_manager.get_updated_props(cache_key)
                if len(updated_props) > 0:
                    response["sideUpdate"] = updated_props
                    has_update = True

                if output_value is callback_manager.UNDEFINED:
                    return to_json(response)
            else:
                try:
                    output_value = _invoke_callback(func, *func_args, **func_kwargs)
                except PreventUpdate as err:
                    raise err
                except Exception as err:  # pylint: disable=broad-exception-caught
                    if error_handler:
                        error_handler(err)
                        if not multi:
                            output_value = NoUpdate()
                        else:
                            output_value = [NoUpdate for _ in output_spec]
                    else:
                        raise err

            component_ids = collections.defaultdict(dict)

            if has_output:
                if not multi:
                    output_value, output_spec = [output_value], [output_spec]
                    flat_output_values = output_value
                else:
                    if isinstance(output_value, (list, tuple)):
                        # For multi-output, allow top-level collection to be
                        # list or tuple
                        output_value = list(output_value)

                    # Flatten grouping and validate grouping structure
                    flat_output_values = flatten_grouping(output_value, output)

                _validate.validate_multi_return(
                    output_spec, flat_output_values, callback_id
                )

                for val, spec in zip(flat_output_values, output_spec):
                    if NoUpdate.is_no_update(val):
                        continue

Review comment (suggestion):
```suggestion
                    if error_handler:
                        output_value = error_handler(err)
                        if not multi:
                            output_value = output_value or NoUpdate()
                        else:
                            output_value = output_value or [NoUpdate() for _ in output_spec]
```

@T4rk1n I feel that we should still allow the dev to return outputs from the error handler -- we should supply the `NoUpdate()` outputs _only_ if the error handler doesn't return anything. (I realize the dev could still update the outputs via `set_props()`, but I don't see any downside to also allowing them to output the "normal" way.) Happy to discuss further if you disagree.

Code after:
                    else:
                        raise exc

                if job_running and output_value is not callback_manager.UNDEFINED:
                    # cached results.
                    callback_manager.terminate_job(job_id)

                if multi and isinstance(output_value, (list, tuple)):
                    output_value = [
                        NoUpdate() if NoUpdate.is_no_update(r) else r
                        for r in output_value
                    ]
                updated_props = callback_manager.get_updated_props(cache_key)
                if len(updated_props) > 0:
                    response["sideUpdate"] = updated_props
                    has_update = True

                if output_value is callback_manager.UNDEFINED:
                    return to_json(response)
            else:
                try:
                    output_value = _invoke_callback(func, *func_args, **func_kwargs)
                except PreventUpdate as err:
                    raise err
                except Exception as err:  # pylint: disable=broad-exception-caught
                    if error_handler:
                        output_value = error_handler(err)

                        # If the error returns nothing, automatically puts NoUpdate for response.
                        if output_value is None:
                            if not multi:
                                output_value = NoUpdate()
                            else:
                                output_value = [NoUpdate for _ in output_spec]
                    else:
                        raise err

            component_ids = collections.defaultdict(dict)

            if has_output:
                if not multi:
                    output_value, output_spec = [output_value], [output_spec]
                    flat_output_values = output_value
                else:
                    if isinstance(output_value, (list, tuple)):
                        # For multi-output, allow top-level collection to be
                        # list or tuple
                        output_value = list(output_value)

                    # Flatten grouping and validate grouping structure
                    flat_output_values = flatten_grouping(output_value, output)