{"record":{"id":"1a7ba23490cdd8f1","repo":"vitessio/vitess","slug":"vt03006","errorCode":"VT03006","errorMessage":"VT03006: column count does not match value count with the row","messagePattern":"VT03006: column count does not match value count with the row","errorType":"error_code","errorClass":"VitessError","httpStatus":null,"severity":"error","filePath":"go/vt/vtgate/planbuilder/operators/insert.go","lineNumber":415,"sourceCode":"\t\troute.Source = insertRowsPlan(ctx, insOp, insStmt, rows)\n\tcase sqlparser.TableStatement:\n\t\top = insertSelectPlan(ctx, insOp, route, insStmt, rows)\n\t}\n\tif insStmt.Comments != nil {\n\t\top = newLockAndComment(op, insStmt.Comments, sqlparser.NoLock)\n\t}\n\treturn op\n}\n\nfunc insertSelectPlan(\n\tctx *plancontext.PlanningContext,\n\tinsOp *Insert,\n\trouteOp *Route,\n\tins *sqlparser.Insert,\n\tsel sqlparser.TableStatement,\n) *InsertSelection {\n\tif columnMismatch(insOp.AutoIncrement, ins, sel) {\n\t\tpanic(vterrors.VT03006())\n\t}\n\n\tselOp, err := PlanQuery(ctx, sel)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\t// output of the select plan will be used to insert rows into the table.\n\tinsertSelect := &InsertSelection{\n\t\tbinaryOperator: newBinaryOp(newLockAndComment(selOp, nil, sqlparser.ShareModeLock), routeOp),\n\t}\n\n\t// When the table we are inserting into also appears in the select, the\n\t// streamed select holds shared locks (it runs with \"lock in share mode\")\n\t// on ranges the insert writes to, which can block or deadlock the insert.\n\t// This flag makes us read the full select result first, so the locks are\n\t// released before the rows are inserted.\n\tinsertTbl := insOp.tableTarget()","sourceCodeStart":397,"sourceCodeEnd":433,"githubUrl":"https://github.com/vitessio/vitess/blob/01a25a7d176f94613b8d59d799f438380a8760e4/go/vt/vtgate/planbuilder/operators/insert.go#L397-L433","documentation":"VT03006 is raised by insertSelectPlan in go/vt/vtgate/planbuilder/operators/insert.go:415 when an `INSERT ... SELECT` statement's SELECT produces a different number of columns than the INSERT's column list (adjusted for auto-increment handling, per the columnMismatch check). Each row the SELECT yields must supply exactly one value per insert column.","triggerScenarios":"Running INSERT INTO t (a, b) SELECT x, y, z FROM other — the select expression count differs from the insert column count; also triggered when auto-increment modification changes expectations but the select still mismatches.","commonSituations":"Hand-written INSERT..SELECT copy queries where the SELECT list was edited independently of the column list; adding an auto-inc column to the insert list without adjusting the SELECT; refactored ETL queries.","solutions":["Make the SELECT expression list match the INSERT column list count exactly","Remove the auto-increment column from the INSERT column list and let Vitess generate it","Run the SELECT alone and compare its column count against the INSERT's column list"],"exampleFix":"// before\nINSERT INTO t (id, name) SELECT pk, first, last FROM other;\n// after\nINSERT INTO t (id, name) SELECT pk, CONCAT(first, ' ', last) FROM other;","handlingStrategy":"validation","validationCode":"// Verify SELECT projection count matches INSERT column count before sending\nfunction checkInsertSelect(insertCols, selectProjections) {\n  if (insertCols.length !== selectProjections.length) {\n    throw new Error(`column count ${insertCols.length} != select count ${selectProjections.length}`);\n  }\n}","typeGuard":"function countsMatch(insertCols, selectCols) {\n  return Array.isArray(insertCols) && Array.isArray(selectCols) && insertCols.length === selectCols.length;\n}","tryCatchPattern":"try {\n  await vtgate.execute(insertSelectSql, args);\n} catch (e) {\n  if (String(e).includes('VT03006')) {\n    console.error('Align the INSERT column list with the SELECT projection list');\n  }\n  throw e;\n}","preventionTips":["Run the SELECT standalone and count its output columns during development","Never list the auto-increment column in INSERT..SELECT column lists","Review INSERT..SELECT column lists whenever either side of the query changes"],"tags":["insert-select","column-count","query-planning"],"backgroundTag":"column-count-mismatch","analyzedSha":"01a25a7d176f94613b8d59d799f438380a8760e4","analyzedAt":"2026-09-01T17:28:30.605Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}