{"record":{"id":"04452df02c6858ba","repo":"apache/beam","slug":"job-finished-in-state-jobapi-jobstate-enum-finalstate","errorCode":null,"errorMessage":"Job finished in state ${jobApi.JobState_Enum[finalState]}","messagePattern":"Job finished in state (.+?)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"sdks/typescript/src/apache_beam/testing/multi_pipeline_runner.ts","lineNumber":116,"sourceCode":"      throw new Error(\"Per-pipeline options not supported.\");\n    }\n    this.mergePipeline(pipeline);\n    return new FakePipelineResult();\n  }\n\n  async reallyRunPipelines() {\n    if (this.allPipelines === undefined) {\n      return new FakePipelineResult();\n    }\n    console.log(this.allPipelines);\n    const pipelineResult = await this.underlying.runPipeline(\n      this.allPipelines,\n      this.options,\n    );\n    const finalState = await pipelineResult.waitUntilFinish();\n    if (finalState != jobApi.JobState_Enum.DONE) {\n      // TODO: Grab the last/most severe error message?\n      throw new Error(\n        \"Job finished in state \" + jobApi.JobState_Enum[finalState],\n      );\n    }\n    this.allPipelines = undefined;\n    return pipelineResult;\n  }\n\n  getPrefix(): string {\n    try {\n      return \"namespace_\" + this.counter + \"_\";\n    } finally {\n      this.counter += 1;\n    }\n  }\n\n  mergePipeline(pipeline: runnerApi.Pipeline) {\n    if (this.allPipelines === undefined) {\n      this.allPipelines = runnerApi.Pipeline.create({","sourceCodeStart":98,"sourceCodeEnd":134,"githubUrl":"https://github.com/apache/beam/blob/12126d8942aaf848030c478b4c6a28c6af861c66/sdks/typescript/src/apache_beam/testing/multi_pipeline_runner.ts#L98-L134","documentation":"reallyRunPipelines() submits all merged pipelines via the universal runner and awaits waitUntilFinish(); if the final state is not DONE it throws 'Job finished in state <STATE>'. Like Runner.run, it signals the batch of pipelines ended in a failed/cancelled/unknown state rather than completing successfully.","triggerScenarios":"Calling runAsync()/reallyRunPipelines() on MultiPipelineRunner when the underlying submitted job terminates in any JobState_Enum other than DONE.","commonSituations":"One of the merged test pipelines fails at runtime on the service, the job is cancelled, or infrastructure errors abort the job.","solutions":["Check job/service logs to find which pipeline or step caused the non-DONE state.","Fix the failing pipeline logic, then re-run the multi-pipeline batch.","Catch the error and inspect the state value to decide whether to retry."],"exampleFix":"// before\nawait multiRunner.runAsync();\n// after\ntry { await multiRunner.runAsync(); }\ncatch (e) { if (e.message.includes(\"Job finished in state\")) console.error(e.message); throw e; }","handlingStrategy":"try-catch","validationCode":"// Ensure all merged pipelines are valid before reallyRunPipelines\nfor (const p of allPipelines) validatePipelineComponents(p);","typeGuard":"function isDone(state: JobState_Enum) { return state === JobState_Enum.DONE; }","tryCatchPattern":"try {\n  await multiRunner.runAsync();\n} catch (e) {\n  if (e.message.startsWith(\"Job finished in state\")) {\n    console.error(\"Multi-pipeline batch failed:\", e.message);\n  } else throw e;\n}","preventionTips":["Ensure each merged pipeline is independently correct before batching","Monitor the service job state and logs","Retry transient infrastructure failures with backoff"],"tags":["job-state","multi-pipeline","pipeline-failure"],"backgroundTag":"invalid-state-transition","analyzedSha":"12126d8942aaf848030c478b4c6a28c6af861c66","analyzedAt":"2026-09-13T01:50:10.254Z","contentChangedAt":"2026-09-13T01:50:10.254Z","schemaVersion":2},"datasetVersion":"2026-09-20T03:17:13.778Z"}