diff --git a/vospace-ui-backend/src/main/java/it/inaf/ia2/vospace/ui/controller/UploadController.java b/vospace-ui-backend/src/main/java/it/inaf/ia2/vospace/ui/controller/UploadController.java
index e3fb16aa4754b8de23964b8b1ae8586f219e4e28..7ddce8eebf291b00147254beac5200bd46fbc5a3 100644
--- a/vospace-ui-backend/src/main/java/it/inaf/ia2/vospace/ui/controller/UploadController.java
+++ b/vospace-ui-backend/src/main/java/it/inaf/ia2/vospace/ui/controller/UploadController.java
@@ -99,13 +99,22 @@ public class UploadController extends BaseController {
             try {
                 String uploadUrl = obtainUploadUrl(nodeUri);
                 result.setUrl(uploadUrl);
-            } catch (VOSpaceException ex) {
-                result.setError(ex.getMessage());
             } catch (Throwable t) {
-                LOG.error("Error while obtaining upload URL for " + nodeUri, t);
-                result.setError("Unable to obtain upload URL");
-            }
+                if (t instanceof VOSpaceException) {
+                    result.setError(t.getMessage());
+                } else {
+                    LOG.error("Error while obtaining upload URL for " + nodeUri, t);
+                    result.setError("Unable to obtain upload URL");
+                }
 
+                try {
+                    // attempt to cleanup node metadata
+                    client.deleteNode(path);
+                } catch (Throwable dt) {
+                    LOG.error("Unable to remove node after failed upload URL retrieval", dt);
+                    result.setError("Retrieval of upload URL failed. Manual cleanup of node metadata may be necessary");
+                }
+            }
             return result;
         }, Runnable::run); // Passing current thread Executor to CompletableFuture to avoid "No thread-bound request found" exception
     }
diff --git a/vospace-ui-backend/src/test/java/it/inaf/ia2/vospace/ui/controller/UploadControllerTest.java b/vospace-ui-backend/src/test/java/it/inaf/ia2/vospace/ui/controller/UploadControllerTest.java
index 50e0459f2488dcceb27bff64e4ecb1ec3793a295..31de6d21f785fa8cd470a03c377b263a994f78fd 100644
--- a/vospace-ui-backend/src/test/java/it/inaf/ia2/vospace/ui/controller/UploadControllerTest.java
+++ b/vospace-ui-backend/src/test/java/it/inaf/ia2/vospace/ui/controller/UploadControllerTest.java
@@ -16,8 +16,11 @@ import static org.hamcrest.core.IsNull.nullValue;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
 import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
 import org.mockito.Mock;
 import static org.mockito.Mockito.doThrow;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
@@ -158,6 +161,8 @@ public class UploadControllerTest {
                 .andExpect(status().isOk())
                 .andExpect(jsonPath("$[0].error").value("Unable to connect"))
                 .andExpect(jsonPath("$[0].url").value(nullValue()));
+
+        verify(client, times(1)).deleteNode(eq("/mynode/test.txt"));
     }
 
     @Test
@@ -177,5 +182,29 @@ public class UploadControllerTest {
                 .andExpect(status().isOk())
                 .andExpect(jsonPath("$[0].error").value("Unable to obtain upload URL"))
                 .andExpect(jsonPath("$[0].url").value(nullValue()));
+
+        verify(client, times(1)).deleteNode(eq("/mynode/test.txt"));
+    }
+
+    @Test
+    public void testUploadUrlErrorWithNodeDeletionError() throws Exception {
+
+        UploadFilesData data = new UploadFilesData();
+        data.setParentPath("/mynode");
+        data.setFiles(List.of("test.txt"));
+
+        doThrow(new NullPointerException()).when(client).getFileServiceEndpoint(any());
+        doThrow(new VOSpaceException("error")).when(client).deleteNode(any());
+
+        mockMvc.perform(post("/preupload")
+                .sessionAttr("user_data", user)
+                .contentType(MediaType.APPLICATION_JSON)
+                .content(MAPPER.writeValueAsString(data)))
+                .andDo(print())
+                .andExpect(status().isOk())
+                .andExpect(jsonPath("$[0].error").value("Retrieval of upload URL failed. Manual cleanup of node metadata may be necessary"))
+                .andExpect(jsonPath("$[0].url").value(nullValue()));
+
+        verify(client, times(1)).deleteNode(eq("/mynode/test.txt"));
     }
 }