diff --git a/dvc/utils/__init__.py b/dvc/utils/__init__.py
--- a/dvc/utils/__init__.py
+++ b/dvc/utils/__init__.py
@@ -476,17 +476,25 @@ def is_exec(mode):
 
 
 def glob_targets(targets, glob=True, recursive=True):
+    from ..exceptions import DvcException
+
     if not glob:
         return targets
 
     from glob import iglob
 
-    return [
+    results = [
         exp_target
         for target in targets
         for exp_target in iglob(target, recursive=recursive)
     ]
 
+    if not results:
+        msg = f"Glob {targets} has no matches."
+        raise DvcException(msg)
+
+    return results
+
 
 def error_handler(func):
     def wrapper(*args, **kwargs):

See https://github.com/iterative/dvc/issues/7462#issuecomment-1070954669
diff --git a/tests/func/test_utils.py b/tests/func/test_utils.py
--- a/tests/func/test_utils.py
+++ b/tests/func/test_utils.py
@@ -1,4 +1,9 @@
+import re
+
+import pytest
+
 from dvc import utils
+from dvc.exceptions import DvcException
 from dvc.fs.local import LocalFileSystem
 
 
@@ -42,3 +47,10 @@ def test_boxify():
     )
 
     assert expected == utils.boxify("message")
+
+
+def test_glob_no_match():
+    with pytest.raises(
+        DvcException, match=re.escape("Glob ['invalid*'] has no matches.")
+    ):
+        utils.glob_targets(["invalid*"], glob=True)

push/pull: globs that match no targets should fail
When using `--glob` with `push` or `pull`, a glob that matches no targets will instead act on all targets. In other words, `dvc push/pull --glob 'nomatches*'` will be the same as `dvc push/pull/repro` without any target argument.

```
$ mkdir repo
$ mkdir dvcremote
$ cd repo
$ git init
Initialized empty Git repository in /private/tmp/glob/repo/.git/
$ dvc init -q
$ dvc remote add -d default ../dvcremote
Setting 'default' as a default remote.
$ echo foo > foo
$ dvc add foo
100% Adding...|████████████████████████████████████████████████|1/1 [00:00, 52.98file/s]

To track the changes with git, run:

    git add foo.dvc .gitignore

To enable auto staging, run:

        dvc config core.autostage true
$ dvc push --glob 'z*'
1 file pushed
$ rm foo
$ dvc pull --glob 'z*'
A       foo
1 file added
```

I would expect the commands to fail or push/pull nothing if there are no `--glob` matches.

tests/func/test_utils.py::test_glob_no_match
tests/func/test_utils.py::test_file_md5_crlf tests/func/test_utils.py::test_dict_md5 tests/func/test_utils.py::test_boxify