diff --git a/src/dir.h b/src/dir.h
index eded6ef35052483a8e053b6ec22b23734b1391f0..a5070a3cda494417a06165f718bf9bdf955e8021 100644
--- a/src/dir.h
+++ b/src/dir.h
@@ -64,8 +64,11 @@ struct dir_output {
    * All other fields/flags should be initialzed to NULL or 0.
    * *item may be overwritten or freed in subsequent calls, so this function
    * should make a copy if necessary.
+   *
+   * The function should return non-zero on error, at which point errno is
+   * assumed to be set to something sensible.
    */
-  void (*item)(struct dir *);
+  int (*item)(struct dir *);
 
   /* Finalizes the output to go to the next program state or exit ncdu. Called
    * after item(NULL) has been called for the root item or before any item()
diff --git a/src/dir_export.c b/src/dir_export.c
index a9d2751befc197fb8ced2141cac9e07b1dc9a9ff..3d32c1af12d77205c47fdea4c744ae9e0383f3ad 100644
--- a/src/dir_export.c
+++ b/src/dir_export.c
@@ -100,15 +100,19 @@ static void output_info(struct dir *d) {
 }
 
 
-/* TODO: Error handling / reporting! */
-static void item(struct dir *item) {
+/* Note on error handling: For convenience, we just keep writing to *stream
+ * without checking the return values of the functions. Only at the and of each
+ * item() call do we check for ferror(). This greatly simplifies the code, but
+ * assumes that calls to fwrite()/fput./etc don't do any weird stuff when
+ * called with a stream that's in an error state. */
+static int item(struct dir *item) {
   if(!item) {
     if(!--level) { /* closing of the root item */
       fputs("]]", stream);
-      fclose(stream);
+      return fclose(stream);
     } else /* closing of a regular directory item */
       fputs("]", stream);
-    return;
+    return ferror(stream);
   }
 
   dir_output.items++;
@@ -123,6 +127,7 @@ static void item(struct dir *item) {
     fputc('[', stream);
 
   output_info(item);
+  return ferror(stream);
 }
 
 
diff --git a/src/dir_mem.c b/src/dir_mem.c
index ce6752517d1662f1df55f3ec9b42c57854224b29..3001c014afa943b430c58c4973b3b34e7983d56f 100644
--- a/src/dir_mem.c
+++ b/src/dir_mem.c
@@ -128,13 +128,13 @@ static void item_add(struct dir *item) {
 }
 
 
-static void item(struct dir *item) {
+static int item(struct dir *item) {
   struct dir *t;
 
   /* Go back to parent dir */
   if(!item) {
     curdir = curdir->parent;
-    return;
+    return 0;
   }
 
   item = item_copy(item);
@@ -165,6 +165,8 @@ static void item(struct dir *item) {
 
   dir_output.size = root->size;
   dir_output.items = root->items;
+
+  return 0;
 }
 
 
diff --git a/src/dir_scan.c b/src/dir_scan.c
index a0efcb3f890b27f8ab27f03d9f9f9355280bb728..ee9b41dfc68bd5ee7380e7abd0b7e07ca0ff09f4 100644
--- a/src/dir_scan.c
+++ b/src/dir_scan.c
@@ -125,16 +125,20 @@ static int dir_scan_recurse(struct dir *d) {
   if(chdir(d->name)) {
     dir_setlasterr(dir_curpath);
     d->flags |= FF_ERR;
-    dir_output.item(d);
-    dir_output.item(NULL);
+    if(dir_output.item(d) || dir_output.item(NULL)) {
+      dir_seterr("Output error: %s", strerror(errno));
+      return 1;
+    }
     return 0;
   }
 
   if((dir = dir_read(&fail)) == NULL) {
     dir_setlasterr(dir_curpath);
     d->flags |= FF_ERR;
-    dir_output.item(d);
-    dir_output.item(NULL);
+    if(dir_output.item(d) || dir_output.item(NULL)) {
+      dir_seterr("Output error: %s", strerror(errno));
+      return 1;
+    }
     if(chdir("..")) {
       dir_seterr("Error going back to parent directory: %s", strerror(errno));
       return 1;
@@ -146,9 +150,15 @@ static int dir_scan_recurse(struct dir *d) {
   if(fail)
     d->flags |= FF_ERR;
 
-  dir_output.item(d);
+  if(dir_output.item(d)) {
+    dir_seterr("Output error: %s", strerror(errno));
+    return 1;
+  }
   fail = dir_walk(dir);
-  dir_output.item(NULL);
+  if(dir_output.item(NULL)) {
+    dir_seterr("Output error: %s", strerror(errno));
+    return 1;
+  }
 
   /* Not being able to chdir back is fatal */
   if(!fail && chdir("..")) {
@@ -190,10 +200,14 @@ static int dir_scan_item(struct dir *d) {
   if(d->flags & FF_DIR && !(d->flags & (FF_ERR|FF_EXL|FF_OTHFS)))
     fail = dir_scan_recurse(d);
   else if(d->flags & FF_DIR) {
-    dir_output.item(d);
-    dir_output.item(NULL);
-  } else
-    dir_output.item(d);
+    if(dir_output.item(d) || dir_output.item(NULL)) {
+      dir_seterr("Output error: %s", strerror(errno));
+      fail = 1;
+    }
+  } else if(dir_output.item(d)) {
+    dir_seterr("Output error: %s", strerror(errno));
+    fail = 1;
+  }
 
   return fail || input_handle(1);
 }
@@ -260,9 +274,16 @@ int dir_scan_process() {
       d->flags |= FF_ERR;
     stat_to_dir(d, &fs);
 
-    dir_output.item(d);
-    fail = dir_walk(dir);
-    dir_output.item(NULL);
+    if(dir_output.item(d)) {
+      dir_seterr("Output error: %s", strerror(errno));
+      fail = 1;
+    }
+    if(!fail)
+      fail = dir_walk(dir);
+    if(!fail && dir_output.item(NULL)) {
+      dir_seterr("Output error: %s", strerror(errno));
+      fail = 1;
+    }
   }
 
   while(dir_fatalerr && !input_handle(0))