{"record":{"id":"9f73f7b446a8a09a","repo":"microsoft/semantic-kernel","slug":"the-parent-process-id-must-be-set-before-scoping-t","errorCode":null,"errorMessage":"The parent process Id must be set before scoping to the parent process.","messagePattern":"The parent process Id must be set before scoping to the parent process\\.","errorType":"exception","errorClass":"InvalidOperationException","httpStatus":null,"severity":"error","filePath":"dotnet/src/Experimental/Process.Runtime.Dapr/Actors/ProcessActor.cs","lineNumber":504,"sourceCode":"    private async Task<DaprProcessInfo> ToDaprProcessInfoAsync()\n    {\n        var processState = new KernelProcessState(this.Name, this._process!.State.Version, this.Id.GetId());\n        var stepTasks = this._steps.Select(step => step.ToDaprStepInfoAsync()).ToList();\n        var steps = await Task.WhenAll(stepTasks).ConfigureAwait(false);\n        return new DaprProcessInfo { InnerStepDotnetType = this._process!.InnerStepDotnetType, Edges = this._process!.Edges, State = processState, Steps = [.. steps] };\n    }\n\n    /// <summary>\n    /// Scopes the Id of a step within the process to the process.\n    /// </summary>\n    /// <param name=\"actorId\">The actor Id to scope.</param>\n    /// <param name=\"scopeToParent\">Indicates if the Id should be scoped to the parent process.</param>\n    /// <returns>A new <see cref=\"ActorId\"/> which is scoped to the process.</returns>\n    private ActorId ScopedActorId(ActorId actorId, bool scopeToParent = false)\n    {\n        if (scopeToParent && string.IsNullOrWhiteSpace(this.ParentProcessId))\n        {\n            throw new InvalidOperationException(\"The parent process Id must be set before scoping to the parent process.\");\n        }\n\n        string id = scopeToParent ? this.ParentProcessId! : this.Id.GetId();\n        return new ActorId($\"{id}.{actorId.GetId()}\");\n    }\n\n    /// <summary>\n    /// Generates a scoped event for the step.\n    /// </summary>\n    /// <param name=\"daprEvent\">The event.</param>\n    /// <returns>A <see cref=\"ProcessEvent\"/> with the correctly scoped namespace.</returns>\n    private ProcessEvent ScopedEvent(ProcessEvent daprEvent)\n    {\n        Verify.NotNull(daprEvent);\n        return daprEvent with { Namespace = $\"{this.Name}_{this._process!.State.Id}\" };\n    }\n\n    #endregion","sourceCodeStart":486,"sourceCodeEnd":522,"githubUrl":"https://github.com/microsoft/semantic-kernel/blob/c028a0c7dc4f0814cdcbaba9d998f187a41197bf/dotnet/src/Experimental/Process.Runtime.Dapr/Actors/ProcessActor.cs#L486-L522","documentation":"ScopedActorId builds a namespaced actor ID by prefixing with either this process's own ID or its parent's ID. When scopeToParent is true, it requires ParentProcessId to be non-empty. This is thrown as an InvalidOperationException (not KernelException) indicating a programming contract violation: a caller requested parent-scoping on a process that has no parent.","triggerScenarios":"ScopedActorId is called with scopeToParent=true when ParentProcessId is null or whitespace. This path is only taken from SendOutgoingPublicEventsAsync (line 460), which itself guards with 'if ParentProcessId is not null' — so the throw indicates ParentProcessId was set to a non-null-but-whitespace value, or the guard and the call are inconsistent at runtime.","commonSituations":"ParentProcessId was assigned an empty string or whitespace during initialization (e.g. parentProcessId argument to InitializeProcessAsync was string.Empty rather than null). Also from a corrupted persisted state where StepParentProcessId was saved as an empty string.","solutions":["Pass null (not string.Empty) as parentProcessId when the process has no parent, so the SendOutgoingPublicEventsAsync guard correctly skips the parent-scoping path.","Inspect the persisted Dapr actor state for ActorStateKeys.StepParentProcessId and ensure it is either a valid GUID/id or absent, never an empty string.","If initializing programmatically, verify that ParentProcessId is set to a real parent actor ID before the process starts executing."],"exampleFix":"// before\nawait proxy.InitializeProcessAsync(processInfo, parentProcessId: \"\"); // empty string\n\n// after\nawait proxy.InitializeProcessAsync(processInfo, parentProcessId: null); // null when no parent","handlingStrategy":"validation","validationCode":"// When initializing a process, pass null (not empty string) when there is no parent:\nstring? parentProcessId = hasParent ? parentActorId : null;\nawait processProxy.InitializeProcessAsync(processInfo, parentProcessId);\n\n// Validate before sending:\nif (string.IsNullOrWhiteSpace(parentProcessId))\n{\n    logger.LogDebug(\"No parent process; skipping parent-scoped operations.\");\n}","typeGuard":null,"tryCatchPattern":"try\n{\n    await process.StartAsync(keepAlive: true);\n}\ncatch (InvalidOperationException ex) when (ex.Message.Contains(\"parent process Id must be set\"))\n{\n    logger.LogError(\"ScopedActorId called with scopeToParent=true but ParentProcessId is not set. Ensure parentProcessId is a valid ID, not empty string.\");\n}","preventionTips":["Pass null (not string.Empty) as parentProcessId when the process has no parent.","Validate that persisted StepParentProcessId in the Dapr state store is either a valid ID or absent.","Never assign whitespace or empty strings to ParentProcessId."],"tags":["dapr","process-runtime","scoping","initialization","semantic-kernel"],"backgroundTag":null,"analyzedSha":"c028a0c7dc4f0814cdcbaba9d998f187a41197bf","analyzedAt":"2026-08-13T13:48:05.040Z","schemaVersion":2},"datasetVersion":"2026-08-13T14:17:21.547Z"}