Index: trunk/native/linux/latest/usr/bin/josm-latest
===================================================================
--- trunk/native/linux/latest/usr/bin/josm-latest	(revision 19636)
+++ trunk/native/linux/latest/usr/bin/josm-latest	(revision 19637)
@@ -58,4 +58,17 @@
     # shellcheck source=/etc/default/josm-latest
     . "/etc/default/${JOSM_VERSION}"
+fi
+
+# Java decodes file names with the character set of the locale, and -Dsun.jnu.encoding
+# cannot override it. If the locale is C/POSIX, or cannot be set at all - typically a
+# UTF-8 LANG whose locale is not installed, as in a Flatpak sandbox - file names are
+# decoded as ASCII and opening a folder that holds a non-ASCII name fails with
+# InvalidPathException. Fall back to C.UTF-8 then, which is built into glibc 2.35 and
+# later. This does not change the language of JOSM: when the locale cannot be set, Java
+# already falls back to English. LC_ALL is needed, as setlocale() fails as a whole if
+# any category names a locale that is not installed.
+if { [ -n "$(locale 2>&1 >/dev/null)" ] || [ "$(locale charmap 2>/dev/null)" = "ANSI_X3.4-1968" ]; } \
+        && [ -z "$(LC_ALL=C.UTF-8 locale 2>&1 >/dev/null)" ]; then
+    export LC_ALL=C.UTF-8
 fi
 
Index: trunk/native/linux/tested/usr/bin/josm
===================================================================
--- trunk/native/linux/tested/usr/bin/josm	(revision 19636)
+++ trunk/native/linux/tested/usr/bin/josm	(revision 19637)
@@ -58,4 +58,17 @@
     # shellcheck source=/etc/default/josm
     . "/etc/default/${JOSM_VERSION}"
+fi
+
+# Java decodes file names with the character set of the locale, and -Dsun.jnu.encoding
+# cannot override it. If the locale is C/POSIX, or cannot be set at all - typically a
+# UTF-8 LANG whose locale is not installed, as in a Flatpak sandbox - file names are
+# decoded as ASCII and opening a folder that holds a non-ASCII name fails with
+# InvalidPathException. Fall back to C.UTF-8 then, which is built into glibc 2.35 and
+# later. This does not change the language of JOSM: when the locale cannot be set, Java
+# already falls back to English. LC_ALL is needed, as setlocale() fails as a whole if
+# any category names a locale that is not installed.
+if { [ -n "$(locale 2>&1 >/dev/null)" ] || [ "$(locale charmap 2>/dev/null)" = "ANSI_X3.4-1968" ]; } \
+        && [ -z "$(LC_ALL=C.UTF-8 locale 2>&1 >/dev/null)" ]; then
+    export LC_ALL=C.UTF-8
 fi
 
Index: trunk/src/org/openstreetmap/josm/gui/bugreport/BugReportDialog.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/bugreport/BugReportDialog.java	(revision 19636)
+++ trunk/src/org/openstreetmap/josm/gui/bugreport/BugReportDialog.java	(revision 19637)
@@ -32,4 +32,5 @@
 import org.openstreetmap.josm.tools.ImageProvider;
 import org.openstreetmap.josm.tools.InputMapUtils;
+import org.openstreetmap.josm.tools.Utils;
 import org.openstreetmap.josm.tools.bugreport.BugReport;
 import org.openstreetmap.josm.tools.bugreport.BugReportQueue.SuppressionMode;
@@ -46,4 +47,6 @@
 public class BugReportDialog extends JDialog {
     private static final int MAX_MESSAGE_SIZE = 500;
+    /** whether the user has been told about file names that cannot be represented, which is done once per session */
+    private static volatile boolean unmappableFileNameExplained;
     // This is explicitly not an ExtendedDialog - we still want to be able to display bug reports if there are problems with preferences/..
     private final JPanel content = new JPanel(new GridBagLayout());
@@ -229,4 +232,20 @@
                     );
             return SuppressionMode.NONE;
+        } else if (e.isUnmappableFileName()) {
+            // Not a bug in JOSM, but a locale which cannot represent the name of a file, see #14596. Explain how to
+            // fix it rather than asking for a bug report, and only once: browsing other folders throws it again.
+            if (!unmappableFileNameExplained) {
+                unmappableFileNameExplained = true;
+                GuiHelper.runInEDTAndWait(() -> JOptionPane.showMessageDialog(MainApplication.getMainFrame(),
+                        tr("<html>JOSM cannot handle the name of a file, because it is running with the character set {0} "
+                                + "for file names, which cannot represent it.<br>"
+                                + "This happens when JOSM is started without a UTF-8 locale, for example with {1}, "
+                                + "or with a locale which is not installed.<br><br>"
+                                + "Please start JOSM with the environment variable {2}.</html>",
+                                Utils.getSystemProperty("sun.jnu.encoding"), "<tt>LANG=C</tt>", "<tt>LC_ALL=C.UTF-8</tt>"),
+                        tr("Unsupported file name"),
+                        JOptionPane.WARNING_MESSAGE));
+            }
+            return SuppressionMode.NONE;
         } else {
             PluginDownloadTask downloadTask = PluginHandler.updateOrdisablePluginAfterException(e);
Index: trunk/src/org/openstreetmap/josm/tools/bugreport/ReportedException.java
===================================================================
--- trunk/src/org/openstreetmap/josm/tools/bugreport/ReportedException.java	(revision 19636)
+++ trunk/src/org/openstreetmap/josm/tools/bugreport/ReportedException.java	(revision 19637)
@@ -5,4 +5,5 @@
 import java.io.Serializable;
 import java.lang.reflect.InvocationTargetException;
+import java.nio.file.InvalidPathException;
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -22,4 +23,5 @@
 import org.openstreetmap.josm.tools.Logging;
 import org.openstreetmap.josm.tools.StreamUtils;
+import org.openstreetmap.josm.tools.Utils;
 
 /**
@@ -279,4 +281,18 @@
 
     /**
+     * Check if this is caused by the name of a file which cannot be represented in the character set Java uses for
+     * file names. Java takes that character set from the locale, so this happens when JOSM runs in the C/POSIX locale,
+     * or with a locale which is not installed: file names are then ASCII only, and opening a folder which holds a
+     * non-ASCII name fails. See #14596.
+     * @return <code>true</code> if it is.
+     * @since 19637
+     */
+    public boolean isUnmappableFileName() {
+        return !"UTF-8".equalsIgnoreCase(Utils.getSystemProperty("sun.jnu.encoding"))
+                && StreamUtils.toStream(CauseTraceIterator::new).anyMatch(t -> t instanceof InvalidPathException
+                        && String.valueOf(((InvalidPathException) t).getReason()).contains("unmappable characters"));
+    }
+
+    /**
      * Iterates over the causes for this exception. Ignores cycles and aborts iteration then.
      * @author Michal Zangl
Index: trunk/test/unit/org/openstreetmap/josm/gui/bugreport/BugReportDialogTest.java
===================================================================
--- trunk/test/unit/org/openstreetmap/josm/gui/bugreport/BugReportDialogTest.java	(revision 19637)
+++ trunk/test/unit/org/openstreetmap/josm/gui/bugreport/BugReportDialogTest.java	(revision 19637)
@@ -0,0 +1,52 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.gui.bugreport;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+import java.nio.file.InvalidPathException;
+import java.util.Collections;
+
+import javax.swing.JOptionPane;
+
+import org.junit.jupiter.api.Test;
+import org.openstreetmap.josm.TestUtils;
+import org.openstreetmap.josm.testutils.mockers.JOptionPaneSimpleMocker;
+import org.openstreetmap.josm.tools.bugreport.BugReport;
+import org.openstreetmap.josm.tools.bugreport.BugReportQueue.SuppressionMode;
+import org.openstreetmap.josm.tools.bugreport.ReportedException;
+
+/**
+ * Unit tests of {@link BugReportDialog}.
+ */
+class BugReportDialogTest {
+
+    /**
+     * A file name the locale cannot represent is explained to the user, once, instead of asking for a bug report.
+     * See #14596.
+     */
+    @Test
+    void testUnmappableFileNameIsExplainedOnce() {
+        TestUtils.assumeWorkingJMockit();
+        final JOptionPaneSimpleMocker mocker = new JOptionPaneSimpleMocker(Collections.singletonMap(
+                "<html>JOSM cannot handle the name of a file, because it is running with the character set ANSI_X3.4-1968 "
+                        + "for file names, which cannot represent it.<br>"
+                        + "This happens when JOSM is started without a UTF-8 locale, for example with <tt>LANG=C</tt>, "
+                        + "or with a locale which is not installed.<br><br>"
+                        + "Please start JOSM with the environment variable <tt>LC_ALL=C.UTF-8</tt>.</html>",
+                JOptionPane.OK_OPTION));
+        final ReportedException e = BugReport.intercept(new InvalidPathException("/home/user/Capture d'écran.png",
+                "Malformed input or input contains unmappable characters"));
+        final String jnuEncoding = System.getProperty("sun.jnu.encoding");
+        try {
+            System.setProperty("sun.jnu.encoding", "ANSI_X3.4-1968");
+            // no bug report dialog: that would fail here, as the tests run headless
+            assertEquals(SuppressionMode.NONE, BugReportDialog.showFor(e, 0));
+            // browsing another folder throws it again, which is not worth another message
+            assertEquals(SuppressionMode.NONE, BugReportDialog.showFor(e, 1));
+        } finally {
+            System.setProperty("sun.jnu.encoding", jnuEncoding);
+        }
+        assertEquals(1, mocker.getInvocationLog().size());
+        assertEquals("Unsupported file name", mocker.getInvocationLog().get(0)[2]);
+    }
+}
Index: trunk/test/unit/org/openstreetmap/josm/tools/bugreport/ReportedExceptionTest.java
===================================================================
--- trunk/test/unit/org/openstreetmap/josm/tools/bugreport/ReportedExceptionTest.java	(revision 19636)
+++ trunk/test/unit/org/openstreetmap/josm/tools/bugreport/ReportedExceptionTest.java	(revision 19637)
@@ -3,5 +3,8 @@
 
 import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
 
+import java.nio.file.InvalidPathException;
 import java.util.Arrays;
 
@@ -62,4 +65,29 @@
 
     /**
+     * Tests {@link ReportedException#isUnmappableFileName()}: a file name which the character set used for file names
+     * cannot represent is recognised, also as the cause of another exception, but only if that character set is not
+     * UTF-8 - otherwise the locale is not to blame. See #14596.
+     */
+    @Test
+    void testIsUnmappableFileName() {
+        // the exception sun.nio.fs.UnixPath throws for "Capture d'écran.png" when the locale is C or not installed
+        InvalidPathException unmappable = new InvalidPathException("/home/user/Capture d'\u00e9cran.png",
+                "Malformed input or input contains unmappable characters");
+        String jnuEncoding = System.getProperty("sun.jnu.encoding");
+        try {
+            System.setProperty("sun.jnu.encoding", "ANSI_X3.4-1968");
+            assertTrue(new ReportedException(unmappable).isUnmappableFileName());
+            assertTrue(new ReportedException(new IllegalStateException(unmappable)).isUnmappableFileName());
+            assertFalse(new ReportedException(new InvalidPathException("a\0b", "Nul character not allowed")).isUnmappableFileName());
+            assertFalse(new ReportedException(new IllegalStateException()).isUnmappableFileName());
+
+            System.setProperty("sun.jnu.encoding", "UTF-8");
+            assertFalse(new ReportedException(unmappable).isUnmappableFileName());
+        } finally {
+            System.setProperty("sun.jnu.encoding", jnuEncoding);
+        }
+    }
+
+    /**
      * Tests that {@link ReportedException#isSame(ReportedException)} works as expected.
      */
