diff --git a/src/common/runnable/run-many-for.test.ts b/src/common/runnable/run-many-for.test.ts
index c2cc152681..768eb57939 100644
--- a/src/common/runnable/run-many-for.test.ts
+++ b/src/common/runnable/run-many-for.test.ts
@@ -223,7 +223,7 @@ describe("runManyFor", () => {
);
return expect(() => runMany()).rejects.toThrow(
- /Tried to get a composite but encountered missing parent ids: "some-runnable-2".\n\nAvailable parent ids are:\n"[0-9a-z-]+",\n"some-runnable-1"/,
+ /Unreachable runnable="some-runnable-1". The specified runAfters; all of "some-runnable-2"; are not part of this injection token/,
);
});
diff --git a/src/common/runnable/run-many-for.ts b/src/common/runnable/run-many-for.ts
index 6f63160b4c..d5bff091eb 100644
--- a/src/common/runnable/run-many-for.ts
+++ b/src/common/runnable/run-many-for.ts
@@ -19,7 +19,8 @@ type Run = (parameter: Param) => Promise | void;
export type RunMany = (injectionToken: InjectionToken, void>) => Run;
-const computedNextEdge = (traversed: string[], graph: Map>, currentId: string) => {
+const computedNextEdge = (traversed: string[], graph: Map>, currentId: string, seenIds: Set) => {
+ seenIds.add(currentId);
const currentNode = graph.get(currentId);
assert(currentNode, `Runnable graph does not contain node with id="${currentId}"`);
@@ -29,13 +30,14 @@ const computedNextEdge = (traversed: string[], graph: Map>,
throw new Error(`Cycle in runnable graph: "${traversed.join(`" -> "`)}" -> "${nextId}"`);
}
- computedNextEdge([...traversed, nextId], graph, nextId);
+ computedNextEdge([...traversed, nextId], graph, nextId, seenIds);
}
};
const verifyRunnablesAreDAG = (runnables: Runnable[]) => {
const rootId = uuid.v4();
const runnableGraph = new Map>();
+ const seenIds = new Set();
// Build the Directed graph
for (const runnable of runnables) {
@@ -52,8 +54,26 @@ const verifyRunnablesAreDAG = (runnables: Runnable[]) => {
}
}
+ getOrInsertSet(runnableGraph, rootId);
+
// Do a DFS to find any cycles
- computedNextEdge([], runnableGraph, rootId);
+ computedNextEdge([], runnableGraph, rootId, seenIds);
+
+ for (const id of runnableGraph.keys()) {
+ if (!seenIds.has(id)) {
+ const runnable = runnables.find(runnable => runnable.id === id);
+
+ assert(runnable, `Unknown runnable id="${id}", logic error`);
+
+ const runAfters = [runnable.runAfter]
+ .flat()
+ .filter(isDefined)
+ .map(runnable => runnable.id)
+ .join('", "');
+
+ throw new Error(`Unreachable runnable="${id}". The specified runAfters; all of "${runAfters}"; are not part of this injection token`);
+ }
+ }
};
const executeRunnableWith = (param: Param) => {