{"record":{"id":"3016192476aea2d6","repo":"apache/seatunnel","slug":"the-physical-plan-didn-t-have-any-can-execute-pipe","errorCode":null,"errorMessage":"The physical plan didn't have any can execute pipeline","messagePattern":"The physical plan didn't have any can execute pipeline","errorType":"exception","errorClass":"UnknownPhysicalPlanException","httpStatus":null,"severity":"error","filePath":"seatunnel-engine/seatunnel-engine-server/src/main/java/org/apache/seatunnel/engine/server/dag/physical/PhysicalPlan.java","lineNumber":123,"sourceCode":"            stateTimestamps[JobStatus.INITIALIZING.ordinal()] = initializationTimestamp;\n            runningJobStateTimestampsIMap.put(jobId, stateTimestamps);\n        }\n\n        if (runningJobStateIMap.get(jobId) == null) {\n            // We must update runningJobStateTimestampsIMap first and then can update\n            // runningJobStateIMap.\n            // Because if a new Master Node become active, we can recover ExecutionState and\n            // PipelineState and JobStatus\n            // from TaskExecutionService. But we can not recover stateTimestamps.\n            stateTimestamps[JobStatus.CREATED.ordinal()] = System.currentTimeMillis();\n            runningJobStateTimestampsIMap.put(jobId, stateTimestamps);\n\n            runningJobStateIMap.put(jobId, JobStatus.CREATED);\n        }\n\n        this.pipelineList = pipelineList;\n        if (pipelineList.isEmpty()) {\n            throw new UnknownPhysicalPlanException(\n                    \"The physical plan didn't have any can execute pipeline\");\n        }\n        this.jobFullName =\n                String.format(\n                        \"Job %s (%s)\",\n                        jobImmutableInformation.getJobConfig().getName(),\n                        jobImmutableInformation.getJobId());\n\n        this.runningJobStateIMap = runningJobStateIMap;\n        this.runningJobStateTimestampsIMap = runningJobStateTimestampsIMap;\n    }\n\n    public void setJobMaster(JobMaster jobMaster) {\n        this.jobMaster = jobMaster;\n        pipelineList.forEach(pipeline -> pipeline.setJobMaster(jobMaster));\n        this.engineConfig = jobMaster.getEngineConfig();\n    }\n","sourceCodeStart":105,"sourceCodeEnd":141,"githubUrl":"https://github.com/apache/seatunnel/blob/cf67b549a7a6c35fa0beb12d83c62892427ea919/seatunnel-engine/seatunnel-engine-server/src/main/java/org/apache/seatunnel/engine/server/dag/physical/PhysicalPlan.java#L105-L141","documentation":"PhysicalPlan's constructor validates that the submitted physical plan contains at least one executable pipeline; an empty pipelineList throws UnknownPhysicalPlanException with the message that the plan has no executable pipeline. A plan with zero pipelines cannot be scheduled, so the engine rejects it at construction time.","triggerScenarios":"Creating a PhysicalPlan from a logical plan that produced no pipelines — e.g., a job config whose DAG has no source/sink after optimization, all pipelines excluded by filters, or a malformed/empty job configuration.","commonSituations":"Job config with only a transform and no source/sink; a config error causing the DAG builder to drop all edges/nodes; programmatically built logical plans with empty pipeline lists; version-dependent planner changes that eliminate pipelines.","solutions":["Review the job config: ensure it contains at least a source and a sink so the DAG yields a pipeline","Run the config through the config validator / dry-run (e.g., seatunnel.sh --check) before submission","Check whether plugin discovery failed so source/sink factories were missing and the planner pruned the DAG — look for earlier warnings in the logs","If building plans programmatically, assert the logical plan's pipeline list is non-empty before constructing PhysicalPlan","Verify the connector plugins are installed in the correct connectors directory for your version"],"exampleFix":"// before\n// config.hocon with only a transform, no source/sink\nenv { parallelism = 1 }\ntransform { Sql { source_table_name = \"x\" } }\n\n// after\nenv { parallelism = 1 }\nsource { FakeSource { result_table_name = \"x\" } }\ntransform { Sql { source_table_name = \"x\" } }\nsink { Console { source_table_name = \"x\" } }","handlingStrategy":"validation","validationCode":"// before submission\nif (logicalPlan == null || logicalPlan.getPipelines().isEmpty()) {\n    throw new IllegalArgumentException(\"Job config produced no pipelines; check source/sink config\");\n}","typeGuard":"boolean hasPipelines(LogicalPlan p) { return p != null && !p.getPipelines().isEmpty(); }","tryCatchPattern":"try {\n    submit(jobConfig);\n} catch (UnknownPhysicalPlanException e) {\n    LOG.error(\"Job produced no pipelines — verify source and sink are configured and plugins installed\", e);\n}","preventionTips":["Always include a source and sink in job configs","Run config validation / --check before submission","Confirm connector plugins are installed for the engine version"],"tags":["zeta-engine","physical-plan","job-config","empty-plan","validation"],"backgroundTag":"empty-result-set","analyzedSha":"cf67b549a7a6c35fa0beb12d83c62892427ea919","analyzedAt":"2026-09-10T21:44:55.265Z","contentChangedAt":"2026-09-10T21:44:55.265Z","schemaVersion":2},"datasetVersion":"2026-09-14T21:17:11.552Z"}