diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml
index 640defde860d57a81d0671f2957b99ded15a3566..361ad7b99a52bbbcec570b639800c175d3c19ab7 100644
--- a/doc/src/sgml/config.sgml
+++ b/doc/src/sgml/config.sgml
@@ -4670,10 +4670,17 @@ COPY postgres_log FROM '/full/path/to/logfile.csv' WITH csv;
 
        <para>
         The value for <varname>search_path</varname> must be a comma-separated
-        list of schema names.  If one of the list items is
-        the special value <literal>$user</literal>, then the schema
-        having the name returned by <function>SESSION_USER</> is substituted, if there
-        is such a schema.  (If not, <literal>$user</literal> is ignored.)
+        list of schema names.  Any name that is not an existing schema, or is
+        a schema for which the user does not have <literal>USAGE</>
+        permission, is silently ignored.
+       </para>
+
+       <para>
+        If one of the list items is the special name
+        <literal>$user</literal>, then the schema having the name returned by
+        <function>SESSION_USER</> is substituted, if there is such a schema
+        and the user has <literal>USAGE</> permission for it.
+        (If not, <literal>$user</literal> is ignored.)
        </para>
 
        <para>
@@ -4697,16 +4704,15 @@ COPY postgres_log FROM '/full/path/to/logfile.csv' WITH csv;
 
        <para>
         When objects are created without specifying a particular target
-        schema, they will be placed in the first schema listed
-        in the search path.  An error is reported if the search path is
-        empty.
+        schema, they will be placed in the first valid schema named in
+        <varname>search_path</varname>.  An error is reported if the search
+        path is empty.
        </para>
 
        <para>
         The default value for this parameter is
-        <literal>'"$user", public'</literal> (where the second part will be
-        ignored if there is no schema named <literal>public</>).
-        This supports shared use of a database (where no users
+        <literal>"$user", public</literal>.
+        This setting supports shared use of a database (where no users
         have private schemas, and all share use of <literal>public</>),
         private per-user schemas, and combinations of these.  Other
         effects can be obtained by altering the default search path
diff --git a/src/backend/catalog/namespace.c b/src/backend/catalog/namespace.c
index dc8f8eaf3f3f60f51fd8b59aa78ccfc36e1b23f9..e92efd863ed74fb77425333d772c194c3d36851b 100644
--- a/src/backend/catalog/namespace.c
+++ b/src/backend/catalog/namespace.c
@@ -3773,14 +3773,12 @@ ResetTempTableNamespace(void)
  * Routines for handling the GUC variable 'search_path'.
  */
 
-/* check_hook: validate new search_path, if possible */
+/* check_hook: validate new search_path value */
 bool
 check_search_path(char **newval, void **extra, GucSource source)
 {
-	bool		result = true;
 	char	   *rawname;
 	List	   *namelist;
-	ListCell   *l;
 
 	/* Need a modifiable copy of string */
 	rawname = pstrdup(*newval);
@@ -3796,52 +3794,17 @@ check_search_path(char **newval, void **extra, GucSource source)
 	}
 
 	/*
-	 * If we aren't inside a transaction, we cannot do database access so
-	 * cannot verify the individual names.	Must accept the list on faith.
+	 * We used to try to check that the named schemas exist, but there are
+	 * many valid use-cases for having search_path settings that include
+	 * schemas that don't exist; and often, we are not inside a transaction
+	 * here and so can't consult the system catalogs anyway.  So now, the only
+	 * requirement is syntactic validity of the identifier list.
 	 */
-	if (IsTransactionState())
-	{
-		/*
-		 * Verify that all the names are either valid namespace names or
-		 * "$user" or "pg_temp".  We do not require $user to correspond to a
-		 * valid namespace, and pg_temp might not exist yet.  We do not check
-		 * for USAGE rights, either; should we?
-		 *
-		 * When source == PGC_S_TEST, we are checking the argument of an ALTER
-		 * DATABASE SET or ALTER USER SET command.	It could be that the
-		 * intended use of the search path is for some other database, so we
-		 * should not error out if it mentions schemas not present in the
-		 * current database.  We issue a NOTICE instead.
-		 */
-		foreach(l, namelist)
-		{
-			char	   *curname = (char *) lfirst(l);
-
-			if (strcmp(curname, "$user") == 0)
-				continue;
-			if (strcmp(curname, "pg_temp") == 0)
-				continue;
-			if (!SearchSysCacheExists1(NAMESPACENAME,
-									   CStringGetDatum(curname)))
-			{
-				if (source == PGC_S_TEST)
-					ereport(NOTICE,
-							(errcode(ERRCODE_UNDEFINED_SCHEMA),
-						   errmsg("schema \"%s\" does not exist", curname)));
-				else
-				{
-					GUC_check_errdetail("schema \"%s\" does not exist", curname);
-					result = false;
-					break;
-				}
-			}
-		}
-	}
 
 	pfree(rawname);
 	list_free(namelist);
 
-	return result;
+	return true;
 }
 
 /* assign_hook: do extra actions as needed */
diff --git a/src/test/regress/expected/guc.out b/src/test/regress/expected/guc.out
index d324862049955574a9fda42799e6cb66248688d8..271706d31e9301147ffa85065ff260db2b60730d 100644
--- a/src/test/regress/expected/guc.out
+++ b/src/test/regress/expected/guc.out
@@ -605,6 +605,31 @@ SELECT current_user = 'temp_reset_user';
 
 DROP ROLE temp_reset_user;
 --
+-- search_path should react to changes in pg_namespace
+--
+set search_path = foo, public, not_there_initially;
+select current_schemas(false);
+ current_schemas 
+-----------------
+ {public}
+(1 row)
+
+create schema not_there_initially;
+select current_schemas(false);
+       current_schemas        
+------------------------------
+ {public,not_there_initially}
+(1 row)
+
+drop schema not_there_initially;
+select current_schemas(false);
+ current_schemas 
+-----------------
+ {public}
+(1 row)
+
+reset search_path;
+--
 -- Tests for function-local GUC settings
 --
 set work_mem = '3MB';
@@ -617,14 +642,7 @@ select report_guc('work_mem'), current_setting('work_mem');
  1MB        | 3MB
 (1 row)
 
--- this should draw only a warning
-alter function report_guc(text) set search_path = no_such_schema;
-NOTICE:  schema "no_such_schema" does not exist
--- with error occurring here
-select report_guc('work_mem'), current_setting('work_mem');
-ERROR:  invalid value for parameter "search_path": "no_such_schema"
-DETAIL:  schema "no_such_schema" does not exist
-alter function report_guc(text) reset search_path set work_mem = '2MB';
+alter function report_guc(text) set work_mem = '2MB';
 select report_guc('work_mem'), current_setting('work_mem');
  report_guc | current_setting 
 ------------+-----------------
diff --git a/src/test/regress/sql/guc.sql b/src/test/regress/sql/guc.sql
index 21ed86f26ba259e7258e6b69afa8f8e80e2a2442..0c217923814893d0aacc2c83d548fe778eeaded7 100644
--- a/src/test/regress/sql/guc.sql
+++ b/src/test/regress/sql/guc.sql
@@ -182,6 +182,18 @@ SELECT relname from pg_class where relname = 'tmp_foo';
 SELECT current_user = 'temp_reset_user';
 DROP ROLE temp_reset_user;
 
+--
+-- search_path should react to changes in pg_namespace
+--
+
+set search_path = foo, public, not_there_initially;
+select current_schemas(false);
+create schema not_there_initially;
+select current_schemas(false);
+drop schema not_there_initially;
+select current_schemas(false);
+reset search_path;
+
 --
 -- Tests for function-local GUC settings
 --
@@ -194,13 +206,7 @@ set work_mem = '1MB';
 
 select report_guc('work_mem'), current_setting('work_mem');
 
--- this should draw only a warning
-alter function report_guc(text) set search_path = no_such_schema;
-
--- with error occurring here
-select report_guc('work_mem'), current_setting('work_mem');
-
-alter function report_guc(text) reset search_path set work_mem = '2MB';
+alter function report_guc(text) set work_mem = '2MB';
 
 select report_guc('work_mem'), current_setting('work_mem');