diff --git a/src/backend/commands/copy.c b/src/backend/commands/copy.c
index cd6b13c09f32d2ba6de59cd174fe9cfd67e95d4c..ac26e811a3eaca127cd7f2741b6e3c25fc298680 100644
--- a/src/backend/commands/copy.c
+++ b/src/backend/commands/copy.c
@@ -176,6 +176,7 @@ typedef struct CopyStateData
 	 */
 	StringInfoData line_buf;
 	bool		line_buf_converted;		/* converted to server encoding? */
+	bool		line_buf_valid;			/* contains the row being processed? */
 
 	/*
 	 * Finally, raw_buf holds raw data read from the data source (file or
@@ -283,7 +284,8 @@ static void CopyFromInsertBatch(CopyState cstate, EState *estate,
 					CommandId mycid, int hi_options,
 					ResultRelInfo *resultRelInfo, TupleTableSlot *myslot,
 					BulkInsertState bistate,
-					int nBufferedTuples, HeapTuple *bufferedTuples);
+					int nBufferedTuples, HeapTuple *bufferedTuples,
+					int firstBufferedLineNo);
 static bool CopyReadLine(CopyState cstate);
 static bool CopyReadLineText(CopyState cstate);
 static int	CopyReadAttributesText(CopyState cstate);
@@ -1776,8 +1778,18 @@ CopyFromErrorCallback(void *arg)
 		}
 		else
 		{
-			/* error is relevant to a particular line */
-			if (cstate->line_buf_converted || !cstate->need_transcoding)
+			/*
+			 * Error is relevant to a particular line.
+			 *
+			 * If line_buf still contains the correct line, and it's already
+			 * transcoded, print it. If it's still in a foreign encoding,
+			 * it's quite likely that the error is precisely a failure to do
+			 * encoding conversion (ie, bad data). We dare not try to convert
+			 * it, and at present there's no way to regurgitate it without
+			 * conversion. So we have to punt and just report the line number.
+			 */
+			if (cstate->line_buf_valid &&
+				(cstate->line_buf_converted || !cstate->need_transcoding))
 			{
 				char	   *lineval;
 
@@ -1788,14 +1800,6 @@ CopyFromErrorCallback(void *arg)
 			}
 			else
 			{
-				/*
-				 * Here, the line buffer is still in a foreign encoding, and
-				 * indeed it's quite likely that the error is precisely a
-				 * failure to do encoding conversion (ie, bad data).  We dare
-				 * not try to convert it, and at present there's no way to
-				 * regurgitate it without conversion.  So we have to punt and
-				 * just report the line number.
-				 */
 				errcontext("COPY %s, line %d",
 						   cstate->cur_relname, cstate->cur_lineno);
 			}
@@ -1865,6 +1869,7 @@ CopyFrom(CopyState cstate)
 #define MAX_BUFFERED_TUPLES 1000
 	HeapTuple  *bufferedTuples = NULL;	/* initialize to silence warning */
 	Size		bufferedTuplesSize = 0;
+	int			firstBufferedLineNo = 0;
 
 	Assert(cstate->rel);
 
@@ -2064,6 +2069,8 @@ CopyFrom(CopyState cstate)
 			if (useHeapMultiInsert)
 			{
 				/* Add this tuple to the tuple buffer */
+				if (nBufferedTuples == 0)
+					firstBufferedLineNo = cstate->cur_lineno;
 				bufferedTuples[nBufferedTuples++] = tuple;
 				bufferedTuplesSize += tuple->t_len;
 
@@ -2078,7 +2085,8 @@ CopyFrom(CopyState cstate)
 				{
 					CopyFromInsertBatch(cstate, estate, mycid, hi_options,
 										resultRelInfo, myslot, bistate,
-										nBufferedTuples, bufferedTuples);
+										nBufferedTuples, bufferedTuples,
+										firstBufferedLineNo);
 					nBufferedTuples = 0;
 					bufferedTuplesSize = 0;
 				}
@@ -2114,7 +2122,8 @@ CopyFrom(CopyState cstate)
 	if (nBufferedTuples > 0)
 		CopyFromInsertBatch(cstate, estate, mycid, hi_options,
 							resultRelInfo, myslot, bistate,
-							nBufferedTuples, bufferedTuples);
+							nBufferedTuples, bufferedTuples,
+							firstBufferedLineNo);
 
 	/* Done, clean up */
 	error_context_stack = errcontext.previous;
@@ -2157,10 +2166,19 @@ static void
 CopyFromInsertBatch(CopyState cstate, EState *estate, CommandId mycid,
 					int hi_options, ResultRelInfo *resultRelInfo,
 					TupleTableSlot *myslot, BulkInsertState bistate,
-					int nBufferedTuples, HeapTuple *bufferedTuples)
+					int nBufferedTuples, HeapTuple *bufferedTuples,
+					int firstBufferedLineNo)
 {
 	MemoryContext oldcontext;
 	int			i;
+	int			save_cur_lineno;
+
+	/*
+	 * Print error context information correctly, if one of the operations
+	 * below fail.
+	 */
+	cstate->line_buf_valid = false;
+	save_cur_lineno = cstate->cur_lineno;
 
 	/*
 	 * heap_multi_insert leaks memory, so switch to short-lived memory context
@@ -2185,6 +2203,7 @@ CopyFromInsertBatch(CopyState cstate, EState *estate, CommandId mycid,
 		{
 			List	   *recheckIndexes;
 
+			cstate->cur_lineno = firstBufferedLineNo + i;
 			ExecStoreTuple(bufferedTuples[i], myslot, InvalidBuffer, false);
 			recheckIndexes =
 				ExecInsertIndexTuples(myslot, &(bufferedTuples[i]->t_self),
@@ -2204,10 +2223,16 @@ CopyFromInsertBatch(CopyState cstate, EState *estate, CommandId mycid,
 			 resultRelInfo->ri_TrigDesc->trig_insert_after_row)
 	{
 		for (i = 0; i < nBufferedTuples; i++)
+		{
+			cstate->cur_lineno = firstBufferedLineNo + i;
 			ExecARInsertTriggers(estate, resultRelInfo,
 								 bufferedTuples[i],
 								 NIL);
+		}
 	}
+
+	/* reset cur_lineno to where we were */
+	cstate->cur_lineno = save_cur_lineno;
 }
 
 /*
@@ -2715,6 +2740,7 @@ CopyReadLine(CopyState cstate)
 	bool		result;
 
 	resetStringInfo(&cstate->line_buf);
+	cstate->line_buf_valid = true;
 
 	/* Mark that encoding conversion hasn't occurred yet */
 	cstate->line_buf_converted = false;