diff --git a/src/backend/utils/cache/inval.c b/src/backend/utils/cache/inval.c
index a09f4d9bcd8c257e43f761eeb1fc7b7b6210298e..a4b10e9aa6da4bfae1a82a019bb19313dbb374e8 100644
--- a/src/backend/utils/cache/inval.c
+++ b/src/backend/utils/cache/inval.c
@@ -461,11 +461,8 @@ RegisterRelcacheInvalidation(Oid dbId, Oid relId)
 	/*
 	 * If the relation being invalidated is one of those cached in the local
 	 * relcache init file, mark that we need to zap that file at commit.
-	 * (Note: perhaps it would be better if this code were a bit more
-	 * decoupled from the knowledge that the init file contains exactly those
-	 * non-shared rels used in catalog caches.)
 	 */
-	if (OidIsValid(dbId) && RelationSupportsSysCache(relId))
+	if (OidIsValid(dbId) && RelationIdIsInInitFile(relId))
 		transInvalInfo->RelcacheInitFileInval = true;
 }
 
diff --git a/src/backend/utils/cache/relcache.c b/src/backend/utils/cache/relcache.c
index a8a19d48502526251ed4debd11a2fdba916cd39e..7ba1cfaf9b057224813f655625ec5a30bbda12cf 100644
--- a/src/backend/utils/cache/relcache.c
+++ b/src/backend/utils/cache/relcache.c
@@ -4231,21 +4231,32 @@ load_relcache_init_file(bool shared)
 	}
 
 	/*
-	 * We reached the end of the init file without apparent problem. Did we
-	 * get the right number of nailed items?  (This is a useful crosscheck in
-	 * case the set of critical rels or indexes changes.)
+	 * We reached the end of the init file without apparent problem.  Did we
+	 * get the right number of nailed items?  This is a useful crosscheck in
+	 * case the set of critical rels or indexes changes.  However, that should
+	 * not happen in a normally-running system, so let's bleat if it does.
 	 */
 	if (shared)
 	{
 		if (nailed_rels != NUM_CRITICAL_SHARED_RELS ||
 			nailed_indexes != NUM_CRITICAL_SHARED_INDEXES)
+		{
+			elog(WARNING, "found %d nailed shared rels and %d nailed shared indexes in init file, but expected %d and %d respectively",
+				 nailed_rels, nailed_indexes,
+				 NUM_CRITICAL_SHARED_RELS, NUM_CRITICAL_SHARED_INDEXES);
 			goto read_failed;
+		}
 	}
 	else
 	{
 		if (nailed_rels != NUM_CRITICAL_LOCAL_RELS ||
 			nailed_indexes != NUM_CRITICAL_LOCAL_INDEXES)
+		{
+			elog(WARNING, "found %d nailed rels and %d nailed indexes in init file, but expected %d and %d respectively",
+				 nailed_rels, nailed_indexes,
+				 NUM_CRITICAL_LOCAL_RELS, NUM_CRITICAL_LOCAL_INDEXES);
 			goto read_failed;
+		}
 	}
 
 	/*
@@ -4363,12 +4374,19 @@ write_relcache_init_file(bool shared)
 		/*
 		 * Ignore if not supposed to be in init file.  We can allow any shared
 		 * relation that's been loaded so far to be in the shared init file,
-		 * but unshared relations must be used for catalog caches.  (Note: if
-		 * you want to change the criterion for rels to be kept in the init
-		 * file, see also inval.c.)
+		 * but unshared relations must be ones that should be in the local
+		 * file per RelationIdIsInInitFile.  (Note: if you want to change the
+		 * criterion for rels to be kept in the init file, see also inval.c.
+		 * The reason for filtering here is to be sure that we don't put
+		 * anything into the local init file for which a relcache inval would
+		 * not cause invalidation of that init file.)
 		 */
-		if (!shared && !RelationSupportsSysCache(RelationGetRelid(rel)))
+		if (!shared && !RelationIdIsInInitFile(RelationGetRelid(rel)))
+		{
+			/* Nailed rels had better get stored. */
+			Assert(!rel->rd_isnailed);
 			continue;
+		}
 
 		/* first write the relcache entry proper */
 		write_item(rel, sizeof(RelationData), fp);
@@ -4484,6 +4502,32 @@ write_item(const void *data, Size len, FILE *fp)
 		elog(FATAL, "could not write init file");
 }
 
+/*
+ * Determine whether a given relation (identified by OID) is one of the ones
+ * we should store in the local relcache init file.
+ *
+ * We must cache all nailed rels, and for efficiency we should cache every rel
+ * that supports a syscache.  The former set is almost but not quite a subset
+ * of the latter.  Currently, we must special-case TriggerRelidNameIndexId,
+ * which RelationCacheInitializePhase3 chooses to nail for efficiency reasons,
+ * but which does not support any syscache.
+ *
+ * Note: this function is currently never called for shared rels.  If it were,
+ * we'd probably also need a special case for DatabaseNameIndexId, which is
+ * critical but does not support a syscache.
+ */
+bool
+RelationIdIsInInitFile(Oid relationId)
+{
+	if (relationId == TriggerRelidNameIndexId)
+	{
+		/* If this Assert fails, we don't need this special case anymore. */
+		Assert(!RelationSupportsSysCache(relationId));
+		return true;
+	}
+	return RelationSupportsSysCache(relationId);
+}
+
 /*
  * Invalidate (remove) the init file during commit of a transaction that
  * changed one or more of the relation cache entries that are kept in the
diff --git a/src/include/utils/relcache.h b/src/include/utils/relcache.h
index 15304209679cdeb3d4b8ca4ac57b06cc4bde61ab..88dbcb9dd61419abbe805c1849ea7bc15bdc41ee 100644
--- a/src/include/utils/relcache.h
+++ b/src/include/utils/relcache.h
@@ -96,6 +96,7 @@ extern void AtEOSubXact_RelationCache(bool isCommit, SubTransactionId mySubid,
 /*
  * Routines to help manage rebuilding of relcache init files
  */
+extern bool RelationIdIsInInitFile(Oid relationId);
 extern void RelationCacheInitFilePreInvalidate(void);
 extern void RelationCacheInitFilePostInvalidate(void);
 extern void RelationCacheInitFileRemove(void);