{"record":{"id":"3f21d6e212bcfb4b","repo":"alibaba/spring-ai-alibaba","slug":"no-generator-found","errorCode":null,"errorMessage":"no generator found!","messagePattern":"no generator found!","errorType":"exception","errorClass":"IllegalStateException","httpStatus":null,"severity":"error","filePath":"spring-ai-alibaba-graph-core/src/main/java/com/alibaba/cloud/ai/graph/async/AsyncGenerator.java","lineNumber":160,"sourceCode":"\t\t\t\treturnValueStack.clear();\n\t\t\t}\n\t\t}\n\n\t\t// private AsyncGenerator.WithResult<E> toGeneratorWithResult( AsyncGenerator<E>\n\t\t// generator ) {\n\t\t// return ( generator instanceof WithResult ) ?\n\t\t// (AsyncGenerator.WithResult<E>) generator :\n\t\t// new WithResult<>(generator);\n\t\t// }\n\n\t\tprotected boolean isLastGenerator() {\n\t\t\treturn generatorsStack.size() == 1;\n\t\t}\n\n\t\t@Override\n\t\tpublic Data<E> next() {\n\t\t\tif (generatorsStack.isEmpty()) { // GUARD\n\t\t\t\tthrow new IllegalStateException(\"no generator found!\");\n\t\t\t}\n\n\t\t\tfinal Embed<E> embed = generatorsStack.peek();\n\t\t\tfinal Data<E> result = embed.generator.next();\n\n\t\t\tif (result.isDone()) {\n\t\t\t\tclearPreviousReturnsValuesIfAny();\n\t\t\t\treturnValueStack.push(result);\n\t\t\t\tif (embed.onCompletion != null /* && result.resultValue != null */ ) {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tembed.onCompletion.accept(result.resultValue);\n\t\t\t\t\t}\n\t\t\t\t\tcatch (Exception e) {\n\t\t\t\t\t\treturn Data.error(e);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif (isLastGenerator()) {\n\t\t\t\t\treturn result;","sourceCodeStart":142,"sourceCodeEnd":178,"githubUrl":"https://github.com/alibaba/spring-ai-alibaba/blob/f82da0b50f35744c13968191be2b1cd2452ef550/spring-ai-alibaba-graph-core/src/main/java/com/alibaba/cloud/ai/graph/async/AsyncGenerator.java#L142-L178","documentation":"AsyncGenerator's internal implementation maintains a stack of embedded generators; next() peeks the top of the stack. If the stack is empty there is no generator to pull from, which is an internal invariant violation, so it throws IllegalStateException('no generator found!').","triggerScenarios":"Calling next() on the AsyncGenerator's internal cursor when no generator was pushed onto generatorsStack — e.g. pulling from the generator after completion or using the cursor API without proper initialization via the enclosing EmbeddingIterator.","commonSituations":"Custom code reaching into AsyncGenerator internals to drive the cursor manually; double-consumption of an already exhausted async generator stream; library bugs in embedded-generator bookkeeping when embedding nested generators.","solutions":["Do not call next() after the generator reported done — check Data.isDone() first","Consume the AsyncGenerator through its public iterator/stream APIs rather than the internal cursor","Ensure each embed/nested generator push is balanced with a pop in your custom extensions","If triggered inside framework code, capture the full stack trace and report with reproduction steps"],"exampleFix":"// before\nData<E> d = cursor.next(); // may throw if exhausted\n// after\nif (!cursor.hasGenerator()) { return; }\nData<E> d = cursor.next();","handlingStrategy":"try-catch","validationCode":"if (cursor == null || cursor.noGenerators()) return; // don't pull","typeGuard":null,"tryCatchPattern":"try { Data<E> d = cursor.next(); } catch (IllegalStateException e) { if (\"no generator found!\".equals(e.getMessage())) { /* stop consumption */ } else throw e; }","preventionTips":["Consume AsyncGenerator only via its public iterator/stream APIs","Never call next() after a done marker","Balance push/pop of embedded generators in custom subclasses"],"tags":["async","generator","internal"],"backgroundTag":"internal-invariant-violation","analyzedSha":"f82da0b50f35744c13968191be2b1cd2452ef550","analyzedAt":"2026-09-09T15:32:42.421Z","contentChangedAt":"2026-09-09T15:32:42.421Z","schemaVersion":2},"datasetVersion":"2026-09-17T15:17:12.973Z"}