diff --git a/frontend/src/metabase-lib/lib/queries/NativeQuery.js b/frontend/src/metabase-lib/lib/queries/NativeQuery.js
index 3b0e3b03e232082c294fa70c2a184170ef884760..ae91c0c85140229749c158fd2b980019e2c3982c 100644
--- a/frontend/src/metabase-lib/lib/queries/NativeQuery.js
+++ b/frontend/src/metabase-lib/lib/queries/NativeQuery.js
@@ -63,7 +63,11 @@ export default class NativeQuery extends AtomicQuery {
   }
 
   canRun() {
-    return this.hasData() && this.queryText().length > 0;
+    return (
+      this.hasData() &&
+      this.queryText().length > 0 &&
+      this.allTemplateTagsAreValid()
+    );
   }
 
   isEmpty() {
@@ -226,6 +230,12 @@ export default class NativeQuery extends AtomicQuery {
   templateTagsMap(): TemplateTags {
     return getIn(this.datasetQuery(), ["native", "template-tags"]) || {};
   }
+  allTemplateTagsAreValid(): boolean {
+    return this.templateTags().every(
+      // field filters require a field
+      t => !(t.type === "dimension" && t.dimension == null),
+    );
+  }
 
   setDatasetQuery(datasetQuery: DatasetQuery): NativeQuery {
     return new NativeQuery(this._originalQuestion, datasetQuery);
@@ -282,7 +292,7 @@ export default class NativeQuery extends AtomicQuery {
               id: Utils.uuid(),
               name: tagName,
               display_name: humanize(tagName),
-              type: null,
+              type: "text",
             };
           }
         }
diff --git a/frontend/src/metabase/query_builder/components/template_tags/TagEditorParam.jsx b/frontend/src/metabase/query_builder/components/template_tags/TagEditorParam.jsx
index 23c4e291ac39fe88a1dcc7bfebae64043ddb86df..7e291f3b37c56e3ae54070c97af9dc810114b380 100644
--- a/frontend/src/metabase/query_builder/components/template_tags/TagEditorParam.jsx
+++ b/frontend/src/metabase/query_builder/components/template_tags/TagEditorParam.jsx
@@ -160,7 +160,12 @@ export default class TagEditorParam extends Component {
 
         {tag.type === "dimension" && (
           <div className="pb1">
-            <h5 className="pb1 text-normal">{t`Field to map to`}</h5>
+            <h5 className="pb1 text-normal">
+              {t`Field to map to`}
+              {tag.dimension == null && (
+                <span className="text-error mx1">(required)</span>
+              )}
+            </h5>
 
             {(!hasSelectedDimensionField ||
               (hasSelectedDimensionField && fieldMetadataLoaded)) && (
@@ -179,41 +184,43 @@ export default class TagEditorParam extends Component {
           </div>
         )}
 
-        <div className="pb1">
-          <h5 className="pb1 text-normal">{t`Filter widget type`}</h5>
-          <Select
-            className="border-med bg-white block"
-            value={tag["widget-type"]}
-            onChange={e =>
-              this.setParameterAttribute("widget-type", e.target.value)
-            }
-            isInitiallyOpen={!tag["widget-type"] && hasWidgetOptions}
-            placeholder={t`Select…`}
-          >
-            {[{ name: "None", type: undefined }]
-              .concat(widgetOptions)
-              .map(widgetOption => (
-                <Option key={widgetOption.type} value={widgetOption.type}>
-                  {widgetOption.name}
-                </Option>
-              ))}
-          </Select>
-          {hasSelectedDimensionField && !hasWidgetOptions && (
-            <p className="pb1">
-              {t`There aren't any filter widgets for this type of field yet.`}{" "}
-              <Link
-                to={MetabaseSettings.docsUrl(
-                  "users-guide/13-sql-parameters",
-                  "the-field-filter-variable-type",
-                )}
-                target="_blank"
-                className="link"
-              >
-                {t`Learn more`}
-              </Link>
-            </p>
-          )}
-        </div>
+        {hasSelectedDimensionField && (
+          <div className="pb1">
+            <h5 className="pb1 text-normal">{t`Filter widget type`}</h5>
+            <Select
+              className="border-med bg-white block"
+              value={tag["widget-type"]}
+              onChange={e =>
+                this.setParameterAttribute("widget-type", e.target.value)
+              }
+              isInitiallyOpen={!tag["widget-type"] && hasWidgetOptions}
+              placeholder={t`Select…`}
+            >
+              {[{ name: "None", type: undefined }]
+                .concat(widgetOptions)
+                .map(widgetOption => (
+                  <Option key={widgetOption.type} value={widgetOption.type}>
+                    {widgetOption.name}
+                  </Option>
+                ))}
+            </Select>
+            {!hasWidgetOptions && (
+              <p className="pb1">
+                {t`There aren't any filter widgets for this type of field yet.`}{" "}
+                <Link
+                  to={MetabaseSettings.docsUrl(
+                    "users-guide/13-sql-parameters",
+                    "the-field-filter-variable-type",
+                  )}
+                  target="_blank"
+                  className="link"
+                >
+                  {t`Learn more`}
+                </Link>
+              </p>
+            )}
+          </div>
+        )}
 
         <div className="flex align-center pb1">
           <h5 className="text-normal mr1">{t`Required?`}</h5>
diff --git a/frontend/test/metabase-lib/lib/queries/NativeQuery.unit.spec.js b/frontend/test/metabase-lib/lib/queries/NativeQuery.unit.spec.js
index 9903ee97c0db230742fc30d4632414b627630157..ca3752e3d5a1229e73afda3023c62ae36443d4ad 100644
--- a/frontend/test/metabase-lib/lib/queries/NativeQuery.unit.spec.js
+++ b/frontend/test/metabase-lib/lib/queries/NativeQuery.unit.spec.js
@@ -1,3 +1,5 @@
+import { assocIn } from "icepick";
+
 import {
   SAMPLE_DATASET,
   MONGO_DATABASE,
@@ -170,5 +172,29 @@ describe("NativeQuery", () => {
         expect(tagMaps["max_price"].display_name).toEqual("Max price");
       });
     });
+    describe("Invalid template tags prevent the query from running", () => {
+      let q = makeQuery().setQueryText("SELECT * from ORDERS where {{foo}}");
+      expect(q.canRun()).toBe(true);
+
+      // set template tag's type to dimension without setting field id
+      q = q.setDatasetQuery(
+        assocIn(
+          q.datasetQuery(),
+          ["native", "template-tags", "foo", "type"],
+          "dimension",
+        ),
+      );
+      expect(q.canRun()).toBe(false);
+
+      // now set the field
+      q = q.setDatasetQuery(
+        assocIn(
+          q.datasetQuery(),
+          ["native", "template-tags", "foo", "dimension"],
+          ["field-id", 123],
+        ),
+      );
+      expect(q.canRun()).toBe(true);
+    });
   });
 });
diff --git a/frontend/test/metabase/public/public.e2e.spec.js b/frontend/test/metabase/public/public.e2e.spec.js
index 88d365a56b180c1f3f10f46cf1b34b63c2efbded..13fade435356052ebea4c7eafef85983f577b7bf 100644
--- a/frontend/test/metabase/public/public.e2e.spec.js
+++ b/frontend/test/metabase/public/public.e2e.spec.js
@@ -37,6 +37,7 @@ import {
   UPDATE_EMBEDDING_PARAMS,
   UPDATE_ENABLE_EMBEDDING,
   UPDATE_TEMPLATE_TAG,
+  SET_IS_SHOWING_TEMPLATE_TAGS_EDITOR,
 } from "metabase/query_builder/actions";
 import NativeQueryEditor from "metabase/query_builder/components/NativeQueryEditor";
 import { delay } from "metabase/lib/promise";
@@ -157,8 +158,11 @@ describe("public/embedded", () => {
         "select count(*) from products where {{category}}",
       );
 
+      await store.waitForActions([SET_IS_SHOWING_TEMPLATE_TAGS_EDITOR]);
       const tagEditorSidebar = app.find(TagEditorSidebar);
 
+      click(tagEditorSidebar.find("SelectButton"));
+
       const fieldFilterVarType = tagEditorSidebar
         .find(".ColumnarSelector-row")
         .at(3);