Index: src/org/openstreetmap/josm/gui/io/UploadPrimitivesTask.java
===================================================================
--- src/org/openstreetmap/josm/gui/io/UploadPrimitivesTask.java	(revision 19126)
+++ src/org/openstreetmap/josm/gui/io/UploadPrimitivesTask.java	(working copy)
@@ -84,10 +84,11 @@
 
     /**
      * Prompt the user about how to proceed.
+     * @param source the source of the changeset exception
      *
      * @return the policy selected by the user
      */
-    protected MaxChangesetSizeExceededPolicy promptUserForPolicy() {
+    protected MaxChangesetSizeExceededPolicy promptUserForPolicy(ChangesetClosedException.Source source) {
         ButtonSpec[] specs = {
                 new ButtonSpec(
                         tr("Continue uploading"),
@@ -109,12 +110,22 @@
                 )
         };
         int numObjectsToUploadLeft = toUpload.getSize() - processedPrimitives.size();
-        String msg1 = tr("The server reported that the current changeset was closed.<br>"
-                + "This is most likely because the changesets size exceeded the max. size<br>"
-                + "of {0} objects on the server ''{1}''.",
-                OsmApi.getOsmApi().getCapabilities().getMaxChangesetSize(),
-                OsmApi.getOsmApi().getBaseUrl()
-        );
+        final String msg1;
+        if (source == ChangesetClosedException.Source.PREPARE_FURTHER_UPLOAD) {
+            msg1 = tr("The current changeset is full.<br>"
+                    + "This is because the changesets size reached the max. size<br>"
+                    + "of {0} objects on the server ''{1}''.",
+                    OsmApi.getOsmApi().getCapabilities().getMaxChangesetSize(),
+                    OsmApi.getOsmApi().getBaseUrl()
+                    );
+        } else {
+            msg1 = tr("The server reported that the current changeset was closed.<br>"
+                    + "This is most likely because the changesets size exceeded the max. size<br>"
+                    + "of {0} objects on the server ''{1}''.",
+                    OsmApi.getOsmApi().getCapabilities().getMaxChangesetSize(),
+                    OsmApi.getOsmApi().getBaseUrl()
+                    );
+        }
         String msg2 = trn(
                 "There is {0} object left to upload.",
                 "There are {0} objects left to upload.",
@@ -150,22 +161,23 @@
     }
 
     /**
-     * Handles a server changeset full response.
+     * Handles a server changeset full response and the case that JOSM detects that a changeset is full while preparing an upload.
      * <p>
-     * Handles a server changeset full response by either aborting or opening a new changeset, if the
+     * Handles a full changeset by either aborting or opening a new changeset, if the
      * user requested it so.
+     * @param source the source of the changeset exception
      *
      * @return true if the upload process should continue with the new changeset, false if the
      *         upload should be interrupted
      * @throws OsmTransferException "if something goes wrong."
      */
-    protected boolean handleChangesetFullResponse() throws OsmTransferException {
+    protected boolean handleChangesetFullResponse(ChangesetClosedException.Source source) throws OsmTransferException {
         if (processedPrimitives.size() == toUpload.getSize()) {
             strategy.setPolicy(MaxChangesetSizeExceededPolicy.ABORT);
             return false;
         }
         if (strategy.getPolicy() == null || strategy.getPolicy() == MaxChangesetSizeExceededPolicy.ABORT) {
-            strategy.setPolicy(promptUserForPolicy());
+            GuiHelper.runInEDTAndWait(() -> strategy.setPolicy(promptUserForPolicy(source)));
         }
         switch (strategy.getPolicy()) {
         case AUTOMATICALLY_OPEN_NEW_CHANGESETS:
@@ -282,9 +294,10 @@
                     }
                     switch (e.getSource()) {
                     case UPLOAD_DATA:
+                    case PREPARE_FURTHER_UPLOAD:
                         // Most likely the changeset is full. Try to recover and continue
                         // with a new changeset, but let the user decide first.
-                        if (handleChangesetFullResponse()) {
+                        if (handleChangesetFullResponse(e.getSource())) {
                             continue;
                         }
                         lastException = e;
@@ -392,7 +405,8 @@
                 if (strategy.getPolicy() == null)
                     /* do nothing if unknown policy */
                     return;
-                if (e.getSource() == ChangesetClosedException.Source.UPLOAD_DATA) {
+                if (e.getSource() == ChangesetClosedException.Source.UPLOAD_DATA
+                        || e.getSource() == ChangesetClosedException.Source.PREPARE_FURTHER_UPLOAD) {
                     switch (strategy.getPolicy()) {
                     case ABORT:
                         break; /* do nothing - we return to map editing */
Index: src/org/openstreetmap/josm/io/ChangesetClosedException.java
===================================================================
--- src/org/openstreetmap/josm/io/ChangesetClosedException.java	(revision 19126)
+++ src/org/openstreetmap/josm/io/ChangesetClosedException.java	(working copy)
@@ -42,10 +42,16 @@
         /**
          * The exception was thrown when data was uploaded to the changeset. This most
          * likely means that the servers capability limits for a changeset have been
-         * exceeded.
+         * exceeded. In this case the changeset itself is not closed, just the connection.
          */
         UPLOAD_DATA,
         /**
+         * The exception was thrown when the servers capability limits for a
+         * changeset would be exceeded and further data should be uploaded.
+         * It is assumed that the server would reject any further upload.
+         */
+        PREPARE_FURTHER_UPLOAD,
+        /**
          * The exception was thrown when we tried to close a changeset.  Probably the changeset
          * already timed out on the server.
          * @since 18283
Index: src/org/openstreetmap/josm/io/OsmServerWriter.java
===================================================================
--- src/org/openstreetmap/josm/io/OsmServerWriter.java	(revision 19126)
+++ src/org/openstreetmap/josm/io/OsmServerWriter.java	(working copy)
@@ -165,19 +165,22 @@
                 if (canceled) return;
                 int j = 0;
                 chunk.clear();
-                while (it.hasNext() && j < chunkSize && processed.size() + j < maxChunkSize) {
+                final int oldChangesCount = api.getChangeset().getChangesCount();
+                while (it.hasNext() && j < chunkSize && oldChangesCount + j < maxChunkSize) {
                     j++;
                     chunk.add(it.next());
                 }
-                progressMonitor.setCustomText(
-                        trn("({0}/{1}) Uploading {2} object...",
-                                "({0}/{1}) Uploading {2} objects...",
-                                chunk.size(), i, numChunks, chunk.size()));
-                processed.addAll(api.uploadDiff(chunk, progressMonitor.createSubTaskMonitor(ProgressMonitor.ALL_TICKS, false)));
-                // see #23738: server will close CS if maximum changeset size was reached
-                if (processed.size() >= maxChunkSize) {
-                    throw new ChangesetClosedException(api.getChangeset().getId(), Instant.now(), Source.UPLOAD_DATA);
+                if (!chunk.isEmpty()) {
+                    progressMonitor.setCustomText(
+                            trn("({0}/{1}) Uploading {2} object...",
+                                    "({0}/{1}) Uploading {2} objects...",
+                                    chunk.size(), i, numChunks, chunk.size()));
+                    processed.addAll(api.uploadDiff(chunk, progressMonitor.createSubTaskMonitor(ProgressMonitor.ALL_TICKS, false)));
                 }
+                // see #23738: server will close the connection when a changeset is too large
+                if (it.hasNext() && oldChangesCount + processed.size() >= maxChunkSize) {
+                    throw new ChangesetClosedException(api.getChangeset().getId(), Instant.now(), Source.PREPARE_FURTHER_UPLOAD);
+                }
             }
         } finally {
             progressMonitor.finishTask();
