Index: trunk/ppStack/src/ppStack.c
===================================================================
--- trunk/ppStack/src/ppStack.c	(revision 27074)
+++ trunk/ppStack/src/ppStack.c	(revision 27075)
@@ -55,57 +55,68 @@
 
 
-     // Common code for the death.
-die:
-    psTrace("ppStack", 1, "Finished at %f sec\n", psTimerMark(TIMER_NAME));
-    psTimerStop();
+ die:
+    // Common code for the death.
+    {
+        psExit exitValue = PS_EXIT_SUCCESS;        // Exit value for program
+        psErrorCode errorCode = psErrorCodeLast(); // Error code
+        if (errorCode != PS_ERR_NONE) {
+            psErrorStackPrint(stderr, "Unable to perform stack.");
+            switch (errorCode) {
+              case PPSTACK_ERR_UNKNOWN:
+              case PS_ERR_UNKNOWN:
+                exitValue = PS_EXIT_UNKNOWN_ERROR;
+                break;
+              case PS_ERR_IO:
+              case PS_ERR_DB_CLIENT:
+              case PS_ERR_DB_SERVER:
+              case PS_ERR_BAD_FITS:
+              case PS_ERR_OS_CALL_FAILED:
+              case PPSTACK_ERR_IO:
+                exitValue = PS_EXIT_SYS_ERROR;
+                break;
+              case PS_ERR_BAD_PARAMETER_VALUE:
+              case PS_ERR_BAD_PARAMETER_TYPE:
+              case PS_ERR_BAD_PARAMETER_NULL:
+              case PS_ERR_BAD_PARAMETER_SIZE:
+              case PPSTACK_ERR_ARGUMENTS:
+              case PPSTACK_ERR_CONFIG:
+                exitValue = PS_EXIT_CONFIG_ERROR;
+                break;
+              case PPSTACK_ERR_PSF:
+              case PPSTACK_ERR_REJECTED:
+              case PPSTACK_ERR_DATA:
+                exitValue = PS_EXIT_DATA_ERROR;
+                break;
+              case PS_ERR_UNEXPECTED_NULL:
+              case PS_ERR_PROGRAMMING:
+              case PPSTACK_ERR_NOT_IMPLEMENTED:
+              case PPSTACK_ERR_PROG:
+              default:
+                // It's a programming error if we're not dealing with the error correctly
+                exitValue = PS_EXIT_PROG_ERROR;
+                break;
+            }
+        }
 
-    psFree(config);
-    pmModelClassCleanup();
-    pmConfigDone();
-    psLibFinalize();
-    pmVisualClose();
+        // Ensure everything closes
+        ppStackFileActivation(config, PPSTACK_FILES_PREPARE, true);
+        ppStackFileActivation(config, PPSTACK_FILES_CONVOLVE, true);
+        ppStackFileActivation(config, PPSTACK_FILES_COMBINE, true);
+        ppStackFileActivation(config, PPSTACK_FILES_PHOT, true);
+        if (!ppStackFilesIterateUp(config)) {
+            psErrorStackPrint(stderr, "Unable to close files.");
+        }
 
-    psExit exitValue = PS_EXIT_SUCCESS;        // Exit value for program
-    psErrorCode errorCode = psErrorCodeLast(); // Error code
-    if (errorCode != PS_ERR_NONE) {
-        psErrorStackPrint(stderr, "Unable to perform stack.");
-        switch (errorCode) {
-          case PPSTACK_ERR_UNKNOWN:
-          case PS_ERR_UNKNOWN:
-            exitValue = PS_EXIT_UNKNOWN_ERROR;
-            break;
-          case PS_ERR_IO:
-          case PS_ERR_DB_CLIENT:
-          case PS_ERR_DB_SERVER:
-          case PS_ERR_BAD_FITS:
-          case PS_ERR_OS_CALL_FAILED:
-          case PPSTACK_ERR_IO:
-            exitValue = PS_EXIT_SYS_ERROR;
-            break;
-          case PS_ERR_BAD_PARAMETER_VALUE:
-          case PS_ERR_BAD_PARAMETER_TYPE:
-          case PS_ERR_BAD_PARAMETER_NULL:
-          case PS_ERR_BAD_PARAMETER_SIZE:
-          case PPSTACK_ERR_ARGUMENTS:
-          case PPSTACK_ERR_CONFIG:
-            exitValue = PS_EXIT_CONFIG_ERROR;
-            break;
-          case PPSTACK_ERR_PSF:
-          case PPSTACK_ERR_REJECTED:
-          case PPSTACK_ERR_DATA:
-            exitValue = PS_EXIT_DATA_ERROR;
-            break;
-          case PS_ERR_UNEXPECTED_NULL:
-          case PS_ERR_PROGRAMMING:
-          case PPSTACK_ERR_NOT_IMPLEMENTED:
-          case PPSTACK_ERR_PROG:
-          default:
-            // It's a programming error if we're not dealing with the error correctly
-            exitValue = PS_EXIT_PROG_ERROR;
-            break;
-        }
+        psTrace("ppStack", 1, "Finished at %f sec\n", psTimerMark(TIMER_NAME));
+        psTimerStop();
+
+        psFree(config);
+        pmModelClassCleanup();
+        pmConfigDone();
+        psLibFinalize();
+        pmVisualClose();
+
+        exit(exitValue);
     }
-
-    exit(exitValue);
 }
 
Index: trunk/ppStack/src/ppStack.h
===================================================================
--- trunk/ppStack/src/ppStack.h	(revision 27074)
+++ trunk/ppStack/src/ppStack.h	(revision 27075)
@@ -13,4 +13,5 @@
 // Mask values for inputs
 typedef enum {
+    PPSTACK_MASK_NONE   = 0x00,         // Nothing wrong
     PPSTACK_MASK_CAL    = 0x01,         // Photometric calibration failed
     PPSTACK_MASK_PSF    = 0x02,         // PSF measurement failed
Index: trunk/ppStack/src/ppStackCamera.c
===================================================================
--- trunk/ppStack/src/ppStackCamera.c	(revision 27074)
+++ trunk/ppStack/src/ppStackCamera.c	(revision 27075)
@@ -284,9 +284,15 @@
 
     if (havePSFs) {
-        pmFPAfile *targetPSF = pmFPAfileDefineOutput(config, output->fpa, "PPSTACK.TARGET.PSF");
+        pmFPA *psfFPA = pmFPAConstruct(config->camera, config->cameraName); // FPA to contain PSF
+        if (!psfFPA) {
+            psError(psErrorCodeLast(), false, "Unable to construct an FPA from camera configuration.");
+            return false;
+        }
+        pmFPAfile *targetPSF = pmFPAfileDefineOutput(config, psfFPA, "PPSTACK.TARGET.PSF");
         if (!targetPSF) {
             psError(psErrorCodeLast(), false, _("Unable to generate output file from PPSTACK.TARGET.PSF"));
             return false;
         }
+        psFree(psfFPA);
         if (targetPSF->type != PM_FPA_FILE_PSF) {
             psError(PPSTACK_ERR_CONFIG, true, "PPSTACK.TARGET.PSF is not of type PSF");
Index: trunk/ppStack/src/ppStackLoop.c
===================================================================
--- trunk/ppStack/src/ppStackLoop.c	(revision 27074)
+++ trunk/ppStack/src/ppStackLoop.c	(revision 27075)
@@ -9,4 +9,46 @@
 #include "ppStack.h"
 #include "ppStackLoop.h"
+
+/// Print a summary of the inputs, and return the number of good inputs
+static int stackSummary(const ppStackOptions *options, // Stack options, with input mask
+                        const char *place              // Place in code
+    )
+{
+    int numGood = 0;                // Number of good inputs
+    psString summary = NULL;        // Summary of images
+    for (int i = 0; i < options->num; i++) {
+        char *reason;               // Reason for rejecting
+        switch (options->inputMask->data.PS_TYPE_VECTOR_MASK_DATA[i]) {
+          case PPSTACK_MASK_NONE:
+            reason = "Good";
+            numGood++;
+            break;
+          case PPSTACK_MASK_CAL:
+            reason = "Calibration failed";
+            break;
+          case PPSTACK_MASK_PSF:
+            reason = "PSF measurement failed";
+            break;
+          case PPSTACK_MASK_MATCH:
+            reason = "PSF matching failed";
+            break;
+          case PPSTACK_MASK_CHI2:
+            reason = "PSF matching chi^2 deviant";
+            break;
+          case PPSTACK_MASK_REJECT:
+            reason = "Rejection exceeded threshold";
+            break;
+          default:
+            psAbort("Unrecognised mask value: %x", options->inputMask->data.PS_TYPE_VECTOR_MASK_DATA[i]);
+        }
+        psStringAppend(&summary, "Image %d: %s\n", i, reason);
+    }
+    psLogMsg("ppStack", PS_LOG_INFO, "Summary of images for %s:\n%s", place, summary);
+    psFree(summary);
+
+    return numGood;
+}
+
+
 
 bool ppStackLoop(pmConfig *config)
@@ -53,4 +95,14 @@
     ppStackMemDump("convolve");
 
+    // Ensure sufficient inputs
+    {
+        int numGood = stackSummary(options, "initial combination");
+        psMetadata *recipe = psMetadataLookupMetadata(NULL, config->recipes, PPSTACK_RECIPE); // ppStack recipe
+        bool safe = psMetadataLookupBool(NULL, recipe, "SAFE"); // Be safe when combining
+        if (safe && numGood <= 1) {
+            psError(PPSTACK_ERR_REJECTED, true, "Insufficient inputs for combination with safety on");
+            return false;
+        }
+    }
 
     // Start threading
@@ -94,4 +146,12 @@
     ppStackMemDump("reject");
 
+    // Check inputs
+    {
+        int numGood = stackSummary(options, "final combination");
+        if (numGood <= 0) {
+            psError(PPSTACK_ERR_REJECTED, true, "Insufficient inputs for combination");
+            return false;
+        }
+    }
 
     // Final combination
Index: trunk/ppStack/src/ppStackPrepare.c
===================================================================
--- trunk/ppStack/src/ppStackPrepare.c	(revision 27074)
+++ trunk/ppStack/src/ppStackPrepare.c	(revision 27075)
@@ -282,5 +282,5 @@
         psLogMsg("ppStack", PS_LOG_INFO, "Target seeing FWHM: %f\n", options->targetSeeing);
 
-        pmChip *outChip = pmFPAfileThisChip(config->files, view, "PPSTACK.OUTPUT"); // Output chip
+        pmChip *outChip = pmFPAfileThisChip(config->files, view, "PPSTACK.TARGET.PSF"); // Output chip
         psMetadataAddPtr(outChip->analysis, PS_LIST_TAIL, "PSPHOT.PSF", PS_DATA_UNKNOWN,
                          "Target PSF", options->psf);
Index: trunk/ppStack/src/ppStackReject.c
===================================================================
--- trunk/ppStack/src/ppStackReject.c	(revision 27074)
+++ trunk/ppStack/src/ppStackReject.c	(revision 27075)
@@ -171,5 +171,5 @@
     psFree(options->regions); options->regions = NULL;
 
-    if (numRejected >= num - 1) {
+    if (numRejected >= num) {
         psError(PPSTACK_ERR_REJECTED, true, "All inputs completely rejected; unable to proceed.");
         return false;
