{"record":{"id":"74445910c5075273","repo":"milvus-io/milvus","slug":"struct-array-q-sub-field-q-length-d-mismatches","errorCode":null,"errorMessage":"struct array %q sub-field %q length %d mismatches first sub-field %q length %d","messagePattern":"struct array %q sub-field %q length (.+?) mismatches first sub-field %q length (.+?)","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"client/column/struct.go","lineNumber":72,"sourceCode":"\nfunc (c *columnStructArray) Type() entity.FieldType {\n\t// Surface as FieldTypeArray to match the user-facing schema field, whose DataType is Array\n\t// and ElementType is Struct. This keeps processInsertColumns' type comparison happy.\n\treturn entity.FieldTypeArray\n}\n\n// Len returns the row count of the struct array. All sub-columns must have identical length;\n// a mismatch indicates data corruption from a failed partial append and is reported via panic\n// with a descriptive message (sub-fields are fully decoupled columns, so this check is the\n// earliest opportunity to surface the invariant violation).\nfunc (c *columnStructArray) Len() int {\n\tif len(c.fields) == 0 {\n\t\treturn 0\n\t}\n\tfirst := c.fields[0].Len()\n\tfor i := 1; i < len(c.fields); i++ {\n\t\tif got := c.fields[i].Len(); got != first {\n\t\t\tpanic(errors.Newf(\"struct array %q sub-field %q length %d mismatches first sub-field %q length %d\",\n\t\t\t\tc.name, c.fields[i].Name(), got, c.fields[0].Name(), first).Error())\n\t\t}\n\t}\n\treturn first\n}\n\nfunc (c *columnStructArray) Slice(start, end int) Column {\n\tfields := make([]Column, len(c.fields))\n\tfor idx, subField := range c.fields {\n\t\tfields[idx] = subField.Slice(start, end)\n\t}\n\treturn &columnStructArray{\n\t\tname:     c.name,\n\t\tfields:   fields,\n\t\tnullable: c.nullable,\n\t}\n}\n","sourceCodeStart":54,"sourceCodeEnd":90,"githubUrl":"https://github.com/milvus-io/milvus/blob/b43a76673a9fe5f01f158979731dc8fd542df81f/client/column/struct.go#L54-L90","documentation":"This panic comes from columnStructArray.Len() (client/column/struct.go:72). A struct-array column wraps one sub-column per struct field, and every sub-column must hold exactly the same number of rows because each row is one struct value appended to all sub-fields in lock-step. Len() treats a length mismatch as data corruption (per its own comment, typically residue from a failed partial append) and panics with the field names and lengths so the earliest invariant violation is surfaced.","triggerScenarios":"1) Building sub-columns by hand (e.g. NewColumnInt64Array, NewColumnVarCharArray) and passing them to NewColumnStructArray when they have different lengths. 2) Appending to one sub-column directly (subCol.Append(...)) instead of going through columnStructArray.AppendValue, which appends to all fields and rolls back on failure. 3) Calling any API that invokes Len() — insert row-count validation, FieldData serialization, Slice — after sub-columns drifted apart.","commonSituations":"Constructing insert data for an Array<Struct> field where one struct member's slice is shorter (e.g. building []string for field A of length N but [][]float32 for field B of length N-1); a partial append failure midway through a row whose rollback was bypassed by custom error handling; code that mutates sub-columns obtained from an existing struct-array column and then reuses the parent.","solutions":["Always append whole rows via columnStructArray.AppendValue(map[string]any{...}) instead of appending to sub-columns individually — it keeps sub-fields in lock-step and rolls back on failure","Before constructing the struct array, assert all sub-column lengths are equal (see validation code) and fix the producer of the short/long slice","If you already have a drifted column, rebuild all sub-columns from your source data rather than trying to patch the mismatched one","Check the error return of every AppendValue call; ignoring it is how partial appends go unnoticed until Len() panics"],"exampleFix":"// before\nint32s := column.NewColumnInt32Array(\"nums\", [][]int32{{1}, {2}})\nnames := column.NewColumnVarCharArray(\"tags\", [][]string{{\"a\"}}) // length 1 != 2\nsa := column.NewColumnStructArray(\"struct_arr\", []column.Column{int32s, names})\n_ = sa.Len() // panics\n\n// after\nsa := column.NewColumnStructArray(\"struct_arr\", []column.Column{int32s, names})\nif err := sa.AppendValue(map[string]any{\"nums\": []int32{3}, \"tags\": []string{\"c\"}}); err != nil {\n    return err\n} // all sub-fields appended atomically, lengths stay equal","handlingStrategy":"validation","validationCode":"// Run before NewColumnStructArray / any insert of Array<Struct> data.\nfunc validateStructArrayFields(name string, fields []column.Column) error {\n    if len(fields) == 0 {\n        return nil\n    }\n    first, firstName := fields[0].Len(), fields[0].Name()\n    for _, f := range fields[1:] {\n        if got := f.Len(); got != first {\n            return fmt.Errorf(\"struct array %q: sub-field %q has %d rows, but %q has %d — equal lengths required\",\n                name, f.Name(), got, firstName, first)\n        }\n    }\n    return nil\n}","typeGuard":null,"tryCatchPattern":"// Only relevant if you call library code that may hit the invariant\n// (e.g. insert with a hand-built struct array column):\nfunc insertStructArraySafe(ctx context.Context, c client.Client, opts client.InsertColumnOption) (err error) {\n    defer func() {\n        if r := recover(); r != nil {\n            err = fmt.Errorf(\"struct array invariant violated: %v\", r)\n        }\n    }()\n    _, err = c.InsertColumns(ctx, opts)\n    return err\n}","preventionTips":["Append rows only through AppendValue(map[string]any{...}) on the struct-array column — never append to sub-columns directly","Always check AppendValue's error return; the rollback it performs is what keeps lengths equal","Add a length-equality assertion in test helpers that build struct-array columns from parallel slices","Treat a mismatch panic as a data-producing bug upstream, not as a library fault to catch and continue"],"tags":["go","milvus-client","panic","struct-array","invariant-violation","insert-data"],"backgroundTag":null,"analyzedSha":"b43a76673a9fe5f01f158979731dc8fd542df81f","analyzedAt":"2026-08-15T10:29:28.408Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}