{"record":{"id":"7649d2a27b30ce90","repo":"qax-os/excelize","slug":"row-d-has-already-been-written","errorCode":null,"errorMessage":"row %d has already been written","messagePattern":"row (.+?) has already been written","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"errors.go","lineNumber":387,"sourceCode":"\treturn fmt.Errorf(\"selected item %s does not exist in pivot table field %s\", item, field)\n}\n\n// newPivotTableRangeError defined the error message on receiving the invalid\n// pivot table range.\nfunc newPivotTableRangeError(msg string) error {\n\treturn fmt.Errorf(\"parameter 'PivotTableRange' parsing error: %s\", msg)\n}\n\n// newPivotTableShowValuesAsBaseFieldError defined the error message on receiving\n// the invalid pivot table \"show values as\" base field.\nfunc newPivotTableShowValuesAsBaseFieldError(field string) error {\n\treturn fmt.Errorf(\"base field %s does not exist in shared items\", field)\n}\n\n// newStreamSetRowError defined the error message on the stream writer\n// receiving the non-ascending row number.\nfunc newStreamSetRowError(row int) error {\n\treturn fmt.Errorf(\"row %d has already been written\", row)\n}\n\n// newStreamSetRowOrderError defined the error message on calling the SetRow\n// function before the order function.\nfunc newStreamSetRowOrderError(name string) error {\n\treturn fmt.Errorf(\"must call the %s function before the SetRow function\", name)\n}\n\n// newUnknownFilterTokenError defined the error message on receiving a unknown\n// filter operator token.\nfunc newUnknownFilterTokenError(token string) error {\n\treturn fmt.Errorf(\"unknown operator: %s\", token)\n}\n\n// newUnsupportedChartType defined the error message on receiving the chart\n// type are unsupported.\nfunc newUnsupportedChartType(chartType ChartType) error {\n\treturn fmt.Errorf(\"unsupported chart type %d\", chartType)","sourceCodeStart":369,"sourceCodeEnd":405,"githubUrl":"https://github.com/qax-os/excelize/blob/f2483381fbfbe432a6baa98d48a1c277183f3285/errors.go#L369-L405","documentation":"The streaming writer (StreamWriter) requires rows to be written in strictly ascending order because rows are serialized incrementally. SetRow was called with a row number that was already flushed to the XML stream, so the library refuses to write it a second time.","triggerScenarios":"Calling sw.SetRow(sheet, row, cellList) twice with the same row number, or calling SetRow with a row number less than the highest previously written row (e.g. writing row 5 then row 3).","commonSituations":"Off-by-one loops (row starting at 1 but SetRow already wrote row 1 as a header); re-writing a header after data rows; retry logic that replays a failed SetRow; merging code paths that both write the same row.","solutions":["Ensure each SetRow call uses a monotonically increasing row number; track the last written row in your loop.","Batch all cells for a row into one SetRow call instead of writing the row twice.","Buffer rows in memory and sort/merge them before writing if your data source is not row-ordered.","If you need random-access writing, use f.SetSheetRow on the regular File API instead of StreamWriter."],"exampleFix":"// before\nfor i, v := range data {\n    sw.SetRow(\"Sheet1\", 0, excelize.Row{Cell: ...})\n    sw.SetRow(\"Sheet1\", i, row) // i==0 collides with header row 0\n}\n// after\nsw.SetRow(\"Sheet1\", 0, headerRow)\nfor i, v := range data {\n    sw.SetRow(\"Sheet1\", i+1, buildRow(v))\n}","handlingStrategy":"validation","validationCode":"lastRow := -1\nwriteRow := func(row int, cells excelize.Row) error {\n    if row <= lastRow {\n        return fmt.Errorf(\"stream row %d not greater than last written %d\", row, lastRow)\n    }\n    lastRow = row\n    return nil\n}","typeGuard":null,"tryCatchPattern":"if err := sw.SetRow(sheet, row, cells); err != nil {\n    if strings.Contains(err.Error(), \"has already been written\") {\n        log.Printf(\"skipping duplicate row %d (stream rows are write-once)\", row)\n        return nil\n    }\n    return err\n}","preventionTips":["Drive row numbers from a single incrementing counter","Never call SetRow twice for the same row; merge cells into one call","Sort data by row before streaming","Keep headers and data in one linear write loop"],"tags":["go","excelize","streamwriter","row-order"],"backgroundTag":"row-already-written","analyzedSha":"f2483381fbfbe432a6baa98d48a1c277183f3285","analyzedAt":"2026-09-02T01:26:19.299Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-09T06:17:21.866Z"}