qax-os/excelize · error
row %d has already been written
Error message
row %d has already been written
What it means
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.
Source
Thrown at errors.go:387
return fmt.Errorf("selected item %s does not exist in pivot table field %s", item, field)
}
// newPivotTableRangeError defined the error message on receiving the invalid
// pivot table range.
func newPivotTableRangeError(msg string) error {
return fmt.Errorf("parameter 'PivotTableRange' parsing error: %s", msg)
}
// newPivotTableShowValuesAsBaseFieldError defined the error message on receiving
// the invalid pivot table "show values as" base field.
func newPivotTableShowValuesAsBaseFieldError(field string) error {
return fmt.Errorf("base field %s does not exist in shared items", field)
}
// newStreamSetRowError defined the error message on the stream writer
// receiving the non-ascending row number.
func newStreamSetRowError(row int) error {
return fmt.Errorf("row %d has already been written", row)
}
// newStreamSetRowOrderError defined the error message on calling the SetRow
// function before the order function.
func newStreamSetRowOrderError(name string) error {
return fmt.Errorf("must call the %s function before the SetRow function", name)
}
// newUnknownFilterTokenError defined the error message on receiving a unknown
// filter operator token.
func newUnknownFilterTokenError(token string) error {
return fmt.Errorf("unknown operator: %s", token)
}
// newUnsupportedChartType defined the error message on receiving the chart
// type are unsupported.
func newUnsupportedChartType(chartType ChartType) error {
return fmt.Errorf("unsupported chart type %d", chartType)View on GitHub (pinned to f2483381fb)
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.
Example fix
// before
for i, v := range data {
sw.SetRow("Sheet1", 0, excelize.Row{Cell: ...})
sw.SetRow("Sheet1", i, row) // i==0 collides with header row 0
}
// after
sw.SetRow("Sheet1", 0, headerRow)
for i, v := range data {
sw.SetRow("Sheet1", i+1, buildRow(v))
} Defensive patterns
Strategy: validation
Validate before calling
lastRow := -1
writeRow := func(row int, cells excelize.Row) error {
if row <= lastRow {
return fmt.Errorf("stream row %d not greater than last written %d", row, lastRow)
}
lastRow = row
return nil
} Try / catch
if err := sw.SetRow(sheet, row, cells); err != nil {
if strings.Contains(err.Error(), "has already been written") {
log.Printf("skipping duplicate row %d (stream rows are write-once)", row)
return nil
}
return err
} Prevention
- 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
When it happens
Trigger: 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).
Common situations: 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.
Related errors
- must call the %s function before the SetRow function
- fill type value must be one of 'gradient' or 'pattern'
- fill color value must be an array of two colors for 'gradien
- fill shading value must be between 0 and 16 for 'gradient' t
- fill color value must be empty or an array of one color for
AI-assisted analysis of qax-os/excelize@f2483381fb (2026-09-02).
Data as JSON: /api/errors/7649d2a27b30ce90.
Report an issue: GitHub.