@@ -30,6 +30,7 @@ import {
3030 csvProxyBodyCapResponse ,
3131 multipartErrorResponse ,
3232 normalizeColumn ,
33+ rowWriteErrorResponse ,
3334} from '@/app/api/table/utils'
3435
3536const logger = createLogger ( 'TableImportCSV' )
@@ -105,12 +106,18 @@ export const POST = withRouteHandler(async (request: NextRequest) => {
105106 headerToColumn : Map < string , string >
106107 }
107108
108- const insertRows = async ( rows : Record < string , unknown > [ ] , state : ImportState ) => {
109+ const insertRows = async (
110+ rows : Record < string , unknown > [ ] ,
111+ state : ImportState ,
112+ currentRowCount : number
113+ ) => {
109114 if ( rows . length === 0 ) return 0
110115 const coerced = coerceRowsForTable ( rows , state . schema , state . headerToColumn )
111116 const result = await batchInsertRows (
112117 { tableId : state . table . id , rows : coerced , workspaceId, userId } ,
113- state . table ,
118+ // The created table's rowCount is frozen at 0; pass the running total so the
119+ // per-batch capacity check sees cumulative rows, not an always-empty table.
120+ { ...state . table , rowCount : currentRowCount } ,
114121 generateId ( ) . slice ( 0 , 8 )
115122 )
116123 return result . length
@@ -132,7 +139,6 @@ export const POST = withRouteHandler(async (request: NextRequest) => {
132139 schema,
133140 workspaceId,
134141 userId,
135- maxRows : planLimits . maxRowsPerTable ,
136142 maxTables : planLimits . maxTables ,
137143 } ,
138144 requestId
@@ -153,13 +159,13 @@ export const POST = withRouteHandler(async (request: NextRequest) => {
153159 sample . push ( record )
154160 if ( sample . length >= CSV_SCHEMA_SAMPLE_SIZE ) {
155161 state = await buildTable ( sample )
156- inserted += await insertRows ( sample , state )
162+ inserted += await insertRows ( sample , state , inserted )
157163 }
158164 continue
159165 }
160166 batch . push ( record )
161167 if ( batch . length >= CSV_MAX_BATCH_SIZE ) {
162- inserted += await insertRows ( batch , state )
168+ inserted += await insertRows ( batch , state , inserted )
163169 batch = [ ]
164170 }
165171 }
@@ -169,9 +175,9 @@ export const POST = withRouteHandler(async (request: NextRequest) => {
169175 return NextResponse . json ( { error : 'CSV file has no data rows' } , { status : 400 } )
170176 }
171177 state = await buildTable ( sample )
172- inserted += await insertRows ( sample , state )
178+ inserted += await insertRows ( sample , state , inserted )
173179 } else {
174- inserted += await insertRows ( batch , state )
180+ inserted += await insertRows ( batch , state , inserted )
175181 }
176182 } catch ( streamError ) {
177183 if ( state ) await deleteTable ( state . table . id , requestId ) . catch ( ( ) => { } )
@@ -200,9 +206,13 @@ export const POST = withRouteHandler(async (request: NextRequest) => {
200206 } catch ( error ) {
201207 if ( isMultipartError ( error ) ) return multipartErrorResponse ( error )
202208
203- const message = toError ( error ) . message
204209 logger . error ( `[${ requestId } ] CSV import failed:` , error )
205210
211+ // Row-write failures (e.g. the plan row-limit check) map to a 400 with the real reason.
212+ const rowWriteError = rowWriteErrorResponse ( error )
213+ if ( rowWriteError ) return rowWriteError
214+
215+ const message = toError ( error ) . message
206216 const isClientError =
207217 message . includes ( 'maximum table limit' ) ||
208218 message . includes ( 'CSV file has no' ) ||
0 commit comments