diff --git a/pkg/tsdb/testdatasource/scenarios.go b/pkg/tsdb/testdatasource/scenarios.go
index 1c4e5b4d26f..e49d760cef2 100644
--- a/pkg/tsdb/testdatasource/scenarios.go
+++ b/pkg/tsdb/testdatasource/scenarios.go
@@ -177,9 +177,12 @@ Timestamps will line up evenly on timeStepSeconds (For example, 60 seconds means
 	})
 
 	s.registerScenario(&Scenario{
-		ID:      string(serverError500Query),
-		Name:    "Server Error (500)",
-		handler: s.handleServerError500Scenario,
+		// Is no longer strictly a _server_ error scenario, but ID is kept for legacy :)
+		ID:          string(serverError500Query),
+		Name:        "Conditional Error",
+		handler:     s.handleServerError500Scenario,
+		StringInput: "1,20,90,30,5,0",
+		Description: "Returns an error when the String Input field is empty",
 	})
 
 	s.registerScenario(&Scenario{
@@ -449,7 +452,19 @@ func (s *Service) handlePredictablePulseScenario(ctx context.Context, req *backe
 }
 
 func (s *Service) handleServerError500Scenario(ctx context.Context, req *backend.QueryDataRequest) (*backend.QueryDataResponse, error) {
-	panic("Test Data Panic!")
+	for _, q := range req.Queries {
+		model, err := simplejson.NewJson(q.JSON)
+		if err != nil {
+			continue
+		}
+
+		stringInput := model.Get("stringInput").MustString()
+		if stringInput == "" {
+			panic("Test Data Panic!")
+		}
+	}
+
+	return s.handleCSVMetricValuesScenario(ctx, req)
 }
 
 func (s *Service) handleClientSideScenario(ctx context.Context, req *backend.QueryDataRequest) (*backend.QueryDataResponse, error) {
diff --git a/public/app/plugins/datasource/testdata/QueryEditor.tsx b/public/app/plugins/datasource/testdata/QueryEditor.tsx
index 1c762c22730..ae68e35ffb8 100644
--- a/public/app/plugins/datasource/testdata/QueryEditor.tsx
+++ b/public/app/plugins/datasource/testdata/QueryEditor.tsx
@@ -21,6 +21,7 @@ import { defaultStreamQuery } from './runStreams';
 import { CSVFileEditor } from './components/CSVFileEditor';
 import { CSVContentEditor } from './components/CSVContentEditor';
 import { USAQueryEditor, usaQueryModes } from './components/USAQueryEditor';
+import ErrorEditor from './components/ErrorEditor';
 
 const showLabelsFor = ['random_walk', 'predictable_pulse'];
 const endpoints = [
@@ -69,6 +70,7 @@ export const QueryEditor = ({ query, datasource, onChange, onRunQuery }: Props)
     [scenarioList, query]
   );
   const scenarioId = currentScenario?.id;
+  const description = currentScenario?.description;
 
   const onScenarioChange = (item: SelectableValue<string>) => {
     const scenario = scenarioList?.find((sc) => sc.id === item.value);
@@ -287,6 +289,9 @@ export const QueryEditor = ({ query, datasource, onChange, onRunQuery }: Props)
       {scenarioId === 'node_graph' && (
         <NodeGraphEditor onChange={(val: NodesQuery) => onChange({ ...query, nodes: val })} query={query} />
       )}
+      {scenarioId === 'server_error_500' && <ErrorEditor onChange={onUpdate} query={query} />}
+
+      {description && <p>{description}</p>}
     </>
   );
 };
diff --git a/public/app/plugins/datasource/testdata/components/ErrorEditor.tsx b/public/app/plugins/datasource/testdata/components/ErrorEditor.tsx
new file mode 100644
index 00000000000..a8447118a88
--- /dev/null
+++ b/public/app/plugins/datasource/testdata/components/ErrorEditor.tsx
@@ -0,0 +1,36 @@
+import { InlineField, InlineFieldRow, Select } from '@grafana/ui';
+import React from 'react';
+import { EditorProps } from '../QueryEditor';
+
+const ERROR_OPTIONS = [
+  {
+    label: 'Server panic',
+    value: 'server_panic',
+  },
+  {
+    label: 'Frontend exception',
+    value: 'frontend_exception',
+  },
+  {
+    label: 'Frontend observable',
+    value: 'frontend_observable',
+  },
+];
+
+const FrontendErrorQueryEditor: React.FC<EditorProps> = ({ query, onChange }) => {
+  return (
+    <InlineFieldRow>
+      <InlineField labelWidth={14} label="Error type">
+        <Select
+          options={ERROR_OPTIONS}
+          value={query.errorType}
+          onChange={(v) => {
+            onChange({ ...query, errorType: v.value });
+          }}
+        />
+      </InlineField>
+    </InlineFieldRow>
+  );
+};
+
+export default FrontendErrorQueryEditor;
diff --git a/public/app/plugins/datasource/testdata/datasource.ts b/public/app/plugins/datasource/testdata/datasource.ts
index 09b8cc133b8..a92f578133f 100644
--- a/public/app/plugins/datasource/testdata/datasource.ts
+++ b/public/app/plugins/datasource/testdata/datasource.ts
@@ -1,4 +1,4 @@
-import { from, merge, Observable, of } from 'rxjs';
+import { from, merge, Observable, of, throwError } from 'rxjs';
 import { delay } from 'rxjs/operators';
 
 import {
@@ -68,6 +68,12 @@ export class TestDataDataSource extends DataSourceWithBackend<TestDataQuery> {
         case 'raw_frame':
           streams.push(this.rawFrameQuery(target, options));
           break;
+        case 'server_error_500':
+          // this now has an option where it can return/throw an error from the frontend.
+          // if it doesn't, send it to the backend where it might panic there :)
+          const query = this.serverErrorQuery(target, options);
+          query ? streams.push(query) : backendQueries.push(target);
+          break;
         // Unusable since 7, removed in 8
         case 'manual_entry': {
           let csvContent = 'Time,Value\n';
@@ -202,6 +208,29 @@ export class TestDataDataSource extends DataSourceWithBackend<TestDataQuery> {
       return of({ data: [], error: ex }).pipe(delay(100));
     }
   }
+
+  serverErrorQuery(
+    target: TestDataQuery,
+    options: DataQueryRequest<TestDataQuery>
+  ): Observable<DataQueryResponse> | null {
+    const { errorType } = target;
+    console.log("we're here!", target);
+
+    if (errorType === 'server_panic') {
+      return null;
+    }
+
+    const stringInput = target.stringInput ?? '';
+    if (stringInput === '') {
+      if (errorType === 'frontend_exception') {
+        throw new Error('Scenario threw an exception in the frontend because the input was empty.');
+      } else {
+        return throwError(() => new Error('Scenario returned an error because the input was empty.'));
+      }
+    }
+
+    return null;
+  }
 }
 
 function runGrafanaAPI(target: TestDataQuery, req: DataQueryRequest<TestDataQuery>): Observable<DataQueryResponse> {
diff --git a/public/app/plugins/datasource/testdata/types.ts b/public/app/plugins/datasource/testdata/types.ts
index f34442fa571..0f23a903de6 100644
--- a/public/app/plugins/datasource/testdata/types.ts
+++ b/public/app/plugins/datasource/testdata/types.ts
@@ -4,6 +4,7 @@ export interface Scenario {
   id: string;
   name: string;
   stringInput: string;
+  description?: string;
 }
 
 export interface TestDataQuery extends DataQuery {
@@ -22,6 +23,7 @@ export interface TestDataQuery extends DataQuery {
   csvContent?: string;
   rawFrameContent?: string;
   usa?: USAQuery;
+  errorType?: 'server_panic' | 'frontend_exception' | 'frontend_observable';
 }
 
 export interface NodesQuery {