PrismLibrary/Prism · error · InvalidOperationException

The DialogCloseCallback has not been properly initialized…

Error message

The DialogCloseCallback has not been properly initialized. This must be initialized by the DialogService, and should not be set by user code.

What it means

DialogCloseListener wraps the callback the DialogService injects into a dialog. Invoking it before/without DialogService initialization leaves the internal callback null (default case), so it throws InvalidOperationException explaining the listener must be set by DialogService and not by user code.

Solutions

  1. Show the dialog via IDialogService.ShowDialogAsync so the listener is injected
  2. Only call RaiseRequestClose after OnDialogOpened has executed
  3. Never assign/construct DialogCloseListener yourself
  4. In tests, set the private callback field via reflection or use the DialogService

Example fix

// before
var dialog = new MyDialog();
dialog.RaiseRequestClose(new DialogResult()); // listener not set
// after
await dialogService.ShowDialogAsync(nameof(MyDialog), parameters, callback);
Defensive patterns

Strategy: try-catch

Validate before calling

// Only close dialogs that went through IDialogService
bool canClose = _dialogOpened; // set true in OnDialogOpened
if (!canClose) return;

Type guard

bool listenerInitialized(IDialogAware dialog) =>
    (bool)(dialog.GetType()
        .GetField("_listener", BindingFlags.NonPublic | BindingFlags.Instance)
        ?.GetValue(dialog) is DialogCloseListener l && l.IsInitialized);

Try / catch

try { RaiseRequestClose(new DialogResult()); }
catch (InvalidOperationException ex) { logger.LogError(ex, "Dialog close attempted before initialization"); }

Prevention

When it happens

Trigger: Calling RaiseRequestClose / DialogCloseListener.Invoke from a dialog view-model whose listener was never injected, e.g. constructing the dialog manually with new instead of through IDialogService, or closing before OnDialogOpened runs.

Common situations: Manually instantiated dialogs in unit tests; calling RaiseRequestClose inside constructors or before the dialog is shown; copying dialog code without the DialogService pipeline.

Understand the failure class

Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.

Related errors


AI-assisted analysis of PrismLibrary/Prism@358118cd64 (2026-09-15). Data as JSON: /api/errors/d0e3d63be9292ef8. Report an issue: GitHub.

Appendix: source

Thrown at src/Prism.Core/Dialogs/DialogCloseListener.cs:72

            Result = result
        });

    /// <summary>
    /// Invokes the initialized delegate with the specified <see cref="IDialogResult"/>
    /// </summary>
    /// <param name="result"></param>
    public async void Invoke(IDialogResult result)
    {
        switch(_callback)
        {
            case Action<IDialogResult> actionCallback:
                actionCallback(result);
                break;
            case Func<IDialogResult, Task> taskCallback:
                await taskCallback(result);
                break;
            default:
                throw new InvalidOperationException("The DialogCloseCallback has not been properly initialized. This must be initialized by the DialogService, and should not be set by user code.");
        }
    }
}

View on GitHub (pinned to 358118cd64)