{"record":{"id":"550829c71da03aab","repo":"vitessio/vitess","slug":"stream-d-for-v-v-is-not-in-stopped-after-copy-s","errorCode":null,"errorMessage":"stream %d for %v.%v is not in Stopped after copy state: %v, %v","messagePattern":"stream (.+?) for (.+?)\\.(.+?) is not in Stopped after copy state: (.+?), (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"go/vt/wrangler/materializer.go","lineNumber":892,"sourceCode":"\t\t\tmessage := row[2].ToString()\n\t\t\tvar bls binlogdatapb.BinlogSource\n\t\t\tsourceBytes, err := row[3].ToBytes()\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\tif err := prototext.Unmarshal(sourceBytes, &bls); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\tif sourceVindex.Owner == \"\" || !bls.StopAfterCopy {\n\t\t\t\t// If there's no owner or we've requested that the workflow NOT be stopped\n\t\t\t\t// after the copy phase completes, then all streams need to be running.\n\t\t\t\tif state != binlogdatapb.VReplicationWorkflowState_Running {\n\t\t\t\t\treturn fmt.Errorf(\"stream %d for %v.%v is not in Running state: %v\", id, targetShard.Keyspace(), targetShard.ShardName(), state)\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\t// If there is an owner, all streams need to be stopped after copy.\n\t\t\t\tif state != binlogdatapb.VReplicationWorkflowState_Stopped || !strings.Contains(message, \"Stopped after copy\") {\n\t\t\t\t\treturn fmt.Errorf(\"stream %d for %v.%v is not in Stopped after copy state: %v, %v\", id, targetShard.Keyspace(), targetShard.ShardName(), state, message)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn nil\n\t})\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tif sourceVindex.Owner != \"\" {\n\t\t// If there is an owner, we have to delete the streams.\n\t\terr := forAllTargets(func(targetShard *topo.ShardInfo) error {\n\t\t\ttargetPrimary, err := wr.ts.GetTablet(ctx, targetShard.PrimaryAlias)\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\tquery := fmt.Sprintf(\"delete from _vt.vreplication where db_name=%s and workflow=%s\", encodeString(targetPrimary.DbName()), encodeString(workflow))\n\t\t\t_, err = wr.tmc.VReplicationExec(ctx, targetPrimary.Tablet, query)","sourceCodeStart":874,"sourceCodeEnd":910,"githubUrl":"https://github.com/vitessio/vitess/blob/01a25a7d176f94613b8d59d799f438380a8760e4/go/vt/wrangler/materializer.go#L874-L910","documentation":"When the vindex being externalized has an owner and StopAfterCopy is set, Wrangler requires every stream to be in Stopped state with a message containing 'Stopped after copy'. Anything else means the streams are not in the expected post-copy quiescent state.","triggerScenarios":"ExternalizeVindex called for an owned vindex whose streams are still Running/Copying, or were stopped for another reason (manual stop, error) so the message lacks 'Stopped after copy'.","commonSituations":"Impatiently externalizing before the auto-stop-after-copy completed; streams stopped manually via Workflow Stop instead of letting the copy phase complete; a stream errored and was stopped.","solutions":["Wait until the copy phase finishes and the streams auto-stop (message shows 'Stopped after copy'), verified via Workflow Show.","If streams are still running, let them catch up; do not stop them manually.","If a stream is in Error, fix the cause and restart the workflow so it can complete copy and stop correctly.","Re-run ExternalizeVindex once all streams report Stopped with 'Stopped after copy'."],"exampleFix":"// before\nvtctldclient Workflow --workflow corder_vdx_vdx Stop\nvtctldclient ExternalizeVindex commerce.corder_vdx  // wrong stop reason\n// after: let workflow stop itself after copy, then\nvtctldclient ExternalizeVindex commerce.corder_vdx","handlingStrategy":"validation","validationCode":"for _, s := range streams {\n  if s.State != binlogdatapb.VReplicationWorkflowState_Stopped ||\n     !strings.Contains(s.Message, \"Stopped after copy\") {\n    return fmt.Errorf(\"stream %d not ready: %s / %s\", s.Id, s.State, s.Message)\n  }\n}","typeGuard":null,"tryCatchPattern":"if err := wr.ExternalizeVindex(ctx, name); err != nil {\n  if strings.Contains(err.Error(), \"Stopped after copy\") {\n    // wait for copy phase to complete and auto-stop before retrying\n  }\n}","preventionTips":["Let owned-vindex workflows stop themselves after copy instead of calling Workflow Stop.","Check stream messages via Workflow Show before externalizing.","Externalize owned vindexes only after copy completion is confirmed."],"tags":["vreplication","workflow-state","vindex"],"backgroundTag":"vreplication-stream-not-stopped-after-copy","analyzedSha":"01a25a7d176f94613b8d59d799f438380a8760e4","analyzedAt":"2026-09-01T17:28:30.605Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}