diff --git a/src/main/java/caosdb/server/CaosDBException.java b/src/main/java/caosdb/server/CaosDBException.java
index adfca3d06f83d990ac540318138093cd0485cf7b..3e0e50769cd3f08423ea83fa851b2cdfca54e50f 100644
--- a/src/main/java/caosdb/server/CaosDBException.java
+++ b/src/main/java/caosdb/server/CaosDBException.java
@@ -22,8 +22,8 @@
  */
 package caosdb.server;
 
-public class CaosDBException extends Exception {
-  /** */
+public class CaosDBException extends RuntimeException {
+
   private static final long serialVersionUID = 5317733089121727021L;
 
   public CaosDBException(final String string) {
diff --git a/src/main/java/caosdb/server/resource/AbstractCaosDBServerResource.java b/src/main/java/caosdb/server/resource/AbstractCaosDBServerResource.java
index 2f4cf5f92d5307f37e5838957f20f7157fdfe7c1..e390d3c55576e774a78e89f17de73ed950b78cf4 100644
--- a/src/main/java/caosdb/server/resource/AbstractCaosDBServerResource.java
+++ b/src/main/java/caosdb/server/resource/AbstractCaosDBServerResource.java
@@ -256,6 +256,10 @@ public abstract class AbstractCaosDBServerResource extends ServerResource {
     this.xslScript = s;
   }
 
+  protected JdomRepresentation ok(Element root) {
+    return ok(new Document(root));
+  }
+
   protected JdomRepresentation ok(final Document doc) {
     return new JdomRepresentation(doc, MediaType.TEXT_XML, "  ", getReference(), getXSLScript());
   }
@@ -339,6 +343,14 @@ public abstract class AbstractCaosDBServerResource extends ServerResource {
     return this.flags;
   }
 
+  protected Element generateRootElement(Element... elements) {
+    Element root = generateRootElement();
+    for (Element e : elements) {
+      root.addContent(e);
+    }
+    return root;
+  }
+
   public static class XMLParser {
     private final LinkedList<SAXBuilder> pool = new LinkedList<SAXBuilder>();
     private final int max = 25;
diff --git a/src/main/java/caosdb/server/resource/ScriptingResource.java b/src/main/java/caosdb/server/resource/ScriptingResource.java
index fa67fac5597116c7eea58a48fdfbd267d20114b6..30a9cdb3fe9e818b0eb67111c0511000b83f3dea 100644
--- a/src/main/java/caosdb/server/resource/ScriptingResource.java
+++ b/src/main/java/caosdb/server/resource/ScriptingResource.java
@@ -22,28 +22,29 @@
  */
 package caosdb.server.resource;
 
-import caosdb.server.CaosDBException;
 import caosdb.server.FileSystem;
 import caosdb.server.accessControl.Principal;
 import caosdb.server.accessControl.SessionToken;
 import caosdb.server.entity.FileProperties;
 import caosdb.server.entity.Message;
-import caosdb.server.scripting.ScriptingUtils;
+import caosdb.server.scripting.CallerSerializer;
 import caosdb.server.scripting.ServerSideScriptingCaller;
+import caosdb.server.utils.Serializer;
+import caosdb.server.utils.ServerMessages;
 import caosdb.server.utils.Utils;
 import com.ibm.icu.text.Collator;
-import java.io.File;
 import java.io.IOException;
 import java.security.NoSuchAlgorithmException;
 import java.util.ArrayList;
+import java.util.Collection;
+import java.util.LinkedList;
 import java.util.List;
+import java.util.logging.Level;
 import java.util.logging.Logger;
 import org.apache.commons.fileupload.FileItemIterator;
 import org.apache.commons.fileupload.FileItemStream;
 import org.apache.commons.fileupload.FileUploadException;
 import org.apache.commons.fileupload.disk.DiskFileItemFactory;
-import org.apache.commons.io.FileUtils;
-import org.jdom2.Document;
 import org.jdom2.Element;
 import org.restlet.data.CharacterSet;
 import org.restlet.data.Form;
@@ -57,94 +58,55 @@ import org.restlet.representation.Representation;
 public class ScriptingResource extends AbstractCaosDBServerResource {
 
   private ServerSideScriptingCaller caller;
+  private Collection<FileProperties> deleteFiles = new LinkedList<>();
 
   @Override
   public Logger getLogger() {
     return Logger.getLogger(this.getClass().getName());
   }
 
-  /*
-   * TODO move to pyinttest
-   */
-  public void setupTestCase(String queryValue) throws IOException {
-    switch (queryValue) {
-      case "call_not_executable":
-        File file1 = new ScriptingUtils().getScriptFile("not_executable");
-        file1.getParentFile().mkdirs();
-        file1.createNewFile();
-        break;
-      case "call_ok":
-        File file2 = new ScriptingUtils().getScriptFile("ok");
-        file2.getParentFile().mkdirs();
-        file2.createNewFile();
-        FileUtils.write(file2, "echo ok");
-        file2.setExecutable(true);
-        break;
-      case "call_err":
-        File file3 = new ScriptingUtils().getScriptFile("err");
-        file3.getParentFile().mkdirs();
-        file3.createNewFile();
-        FileUtils.write(file3, ">&2 echo \"err\"; exit 1");
-        file3.setExecutable(true);
-        break;
-      default:
-        break;
-    }
-  }
+  public Element generateRootElement(ServerSideScriptingCaller caller) {
+    Serializer<ServerSideScriptingCaller, Element> xmlSerializer = new CallerSerializer();
+    Element callerElement = xmlSerializer.serialize(caller);
 
-  /*
-   * TODO move to serialization class
-   */
-  public Element generateResponse(
-      Integer code, String[] commandLine, String stdOut, String stdErr) {
-    Element callElem = new Element("call");
-    callElem.setText(String.join(" ", commandLine));
-
-    Element stdErrElem = new Element("stderr");
-    stdErrElem.addContent(stdErr);
-
-    Element stdOutElem = new Element("stdout");
-    stdOutElem.addContent(stdOut);
-
-    Element scriptElem = new Element("script");
-    scriptElem.setAttribute("code", code.toString());
-    scriptElem.addContent(callElem);
-    scriptElem.addContent(stdOutElem);
-    scriptElem.addContent(stdErrElem);
-
-    Element root = generateRootElement();
-    root.addContent(scriptElem);
-    return root;
+    return generateRootElement(callerElement);
   }
 
   @Override
   protected Representation httpPostInChildClass(Representation entity) throws Exception {
 
     MediaType mediaType = entity.getMediaType();
-    int code;
     try {
       if (mediaType.equals(MediaType.MULTIPART_FORM_DATA, true)) {
-        code = handleMultiparts(entity);
+        handleMultiparts(entity);
       } else if (mediaType.equals(MediaType.APPLICATION_WWW_FORM)) {
-        code = handleForm(new Form(entity));
+        handleForm(new Form(entity));
       } else {
         getResponse().setStatus(Status.CLIENT_ERROR_UNSUPPORTED_MEDIA_TYPE);
         return null;
       }
     } catch (Message m) {
       return error(m, Status.valueOf(m.getCode()));
+    } finally {
+      deleteTmpFiles();
+    }
+    return ok(generateRootElement(this.caller));
+  }
+
+  private void deleteTmpFiles() {
+    for (FileProperties p : deleteFiles) {
+      try {
+        p.deleteFile();
+      } catch (Exception t) {
+        if (getLogger().isLoggable(Level.WARNING)) {
+          getLogger().warning("Could not delete tmp file: " + t.toString());
+        }
+      }
     }
-    return ok(
-        new Document(
-            generateResponse(
-                code,
-                this.caller.getCommandLine(),
-                this.caller.getStdOut(),
-                this.caller.getStdErr())));
   }
 
   public int handleMultiparts(Representation entity)
-      throws FileUploadException, IOException, NoSuchAlgorithmException, CaosDBException, Message {
+      throws FileUploadException, IOException, NoSuchAlgorithmException, Message {
     final DiskFileItemFactory factory = new DiskFileItemFactory();
     factory.setSizeThreshold(1000240);
     final RestletFileUpload upload = new RestletFileUpload(factory);
@@ -158,11 +120,15 @@ public class ScriptingResource extends AbstractCaosDBServerResource {
       if (item.isFormField()) {
         // put plain text form field into the form
         CharacterSet characterSet = ContentType.readCharacterSet(item.getContentType());
-        String value = Utils.InputStream2String(item.openStream(), (characterSet!=null?characterSet.toString():CharacterSet.UTF_8.toString()));
+        String value =
+            Utils.InputStream2String(
+                item.openStream(),
+                (characterSet != null ? characterSet.toString() : CharacterSet.UTF_8.toString()));
         form.add(new Parameter(item.getFieldName(), value));
       } else {
         // -> this is a file, store in tmp dir
         final FileProperties file = FileSystem.upload(item, this.getSRID());
+        deleteTmpFileAfterTermination(file);
         file.setPath(item.getName());
         file.setTmpIdentifyer(item.getFieldName());
         files.add(file);
@@ -175,11 +141,15 @@ public class ScriptingResource extends AbstractCaosDBServerResource {
     return callScript(form, files);
   }
 
+  private void deleteTmpFileAfterTermination(FileProperties file) {
+    deleteFiles.add(file);
+  }
+
   public int handleForm(Form form) throws Message {
     return callScript(form, null);
   }
 
-  public List<String> form2CommandLine(Form form) {
+  public List<String> form2CommandLine(Form form) throws Message {
     ArrayList<String> commandLine = new ArrayList<>(form.size());
     ArrayList<Parameter> positionalArgs = new ArrayList<>(form.size() - 1);
 
@@ -203,7 +173,7 @@ public class ScriptingResource extends AbstractCaosDBServerResource {
 
     if (commandLine.get(0).length() == 0) {
       // first item still empty
-      throw new RuntimeException("Missing form field `call`.");
+      throw ServerMessages.SERVER_SIDE_SCRIPT_MISSING_CALL;
     }
     return commandLine;
   }
diff --git a/src/main/java/caosdb/server/scripting/CallerSerializer.java b/src/main/java/caosdb/server/scripting/CallerSerializer.java
new file mode 100644
index 0000000000000000000000000000000000000000..1bd265650bed0d5de865cc91b29db3327b56174b
--- /dev/null
+++ b/src/main/java/caosdb/server/scripting/CallerSerializer.java
@@ -0,0 +1,36 @@
+package caosdb.server.scripting;
+
+import caosdb.server.CaosDBException;
+import caosdb.server.utils.Serializer;
+import java.io.IOException;
+import org.jdom2.Element;
+
+public class CallerSerializer implements Serializer<ServerSideScriptingCaller, Element> {
+
+  @Override
+  public Element serialize(ServerSideScriptingCaller caller) {
+    Element command = new Element("call");
+    command.setText(String.join(" ", caller.getCommandLine()));
+
+    Element stdout = new Element("stdout");
+    try {
+      stdout.addContent(caller.getStdOut());
+    } catch (IOException e) {
+      throw new CaosDBException(e);
+    }
+
+    Element stderr = new Element("stderr");
+    try {
+      stderr.addContent(caller.getStdErr());
+    } catch (IOException e) {
+      throw new CaosDBException(e);
+    }
+
+    Element script = new Element("script");
+    script.setAttribute("code", caller.getCode().toString());
+    script.addContent(command);
+    script.addContent(stdout);
+    script.addContent(stderr);
+    return script;
+  }
+}
diff --git a/src/main/java/caosdb/server/scripting/ServerSideScriptingCaller.java b/src/main/java/caosdb/server/scripting/ServerSideScriptingCaller.java
index 61e914ba0aca24d50412141ea23eb55fe673f6ff..2455916ec6245f1e721908b6e91f65b230b60a3a 100644
--- a/src/main/java/caosdb/server/scripting/ServerSideScriptingCaller.java
+++ b/src/main/java/caosdb/server/scripting/ServerSideScriptingCaller.java
@@ -37,6 +37,7 @@ import org.apache.commons.io.FileUtils;
 public class ServerSideScriptingCaller {
 
   public static final String UPLOAD_FILES_DIR = ".upload_files";
+  public static final Integer STARTED = -1;
   private final String[] commandLine;
   private final int timeoutMs;
   private ScriptingUtils utils;
@@ -47,6 +48,11 @@ public class ServerSideScriptingCaller {
   private File stdErrFile;
   private String stdErr = null;
   private String stdOut;
+  private Integer code = null;
+
+  public Integer getCode() {
+    return code;
+  }
 
   File getUploadFilesDir() {
     return getTmpWorkingDir().toPath().resolve(UPLOAD_FILES_DIR).toFile();
@@ -188,9 +194,11 @@ public class ServerSideScriptingCaller {
     pb.redirectError(Redirect.to(getStdErrFile()));
     pb.directory(getTmpWorkingDir());
 
+    code = STARTED;
     final TimeoutProcess process = new TimeoutProcess(pb.start(), getTimeoutMs());
 
-    return process.waitFor();
+    code = process.waitFor();
+    return code;
   }
 
   public File getStdOutFile() {
diff --git a/src/main/java/caosdb/server/utils/Serializer.java b/src/main/java/caosdb/server/utils/Serializer.java
new file mode 100644
index 0000000000000000000000000000000000000000..5508e99388a9e4191fd524ccbfc3e30007f6759a
--- /dev/null
+++ b/src/main/java/caosdb/server/utils/Serializer.java
@@ -0,0 +1,6 @@
+package caosdb.server.utils;
+
+public interface Serializer<T, S> {
+
+  public S serialize(T object);
+}
diff --git a/src/test/java/caosdb/server/resource/TestScriptingResource.java b/src/test/java/caosdb/server/resource/TestScriptingResource.java
index 713d30b981c5d0f6b9fc58ea550d084c192c0313..3b035fe703409605c59a77df70421e7516230376 100644
--- a/src/test/java/caosdb/server/resource/TestScriptingResource.java
+++ b/src/test/java/caosdb/server/resource/TestScriptingResource.java
@@ -71,7 +71,7 @@ public class TestScriptingResource {
   }
 
   @Test
-  public void testForm2invocation() {
+  public void testForm2invocation() throws Message {
     Form form =
         new Form(
             "-Ooption=OPTION&call=CA%20LL&-Ooption2=OPTION2&-p=POS1&-p4=POS3&-p2=POS2&IGNORED");