Repository: apache/airflow

Issue:
Airflow UI - Go button on Tree View causes a Invalid Date
**Apache Airflow version**:
1.10.10

**Kubernetes version (if you are using kubernetes)** (use `kubectl version`):

kubeenetes ver: 1.15.5

**Environment**:

- **Cloud provider or hardware configuration**: Used docker based Airflow Image
- **OS** (e.g. from /etc/os-release): Debian
- **Kernel** (e.g. `uname -a`): 4.19.76-linuxkit x86_64 Linux


**What happened**:

When user navigates to tree view of dag and keep on clicking on `Go` button at `number of runs` the` base date` cycle back to one dag run and in the end when there is no dag run left, it shows `invalid date`. In the end user gets below exception: 

```
                          ____/ (  (    )   )  \___
                         /( (  (  )   _    ))  )   )\
                       ((     (   )(    )  )   (   )  )
                     ((/  ( _(   )   (   _) ) (  () )  )
                    ( (  ( (_)   ((    (   )  .((_ ) .  )_
                   ( (  )    (      (  )    )   ) . ) (   )
                  (  (   (  (   ) (  _  ( _) ).  ) . ) ) ( )
                  ( (  (   ) (  )   (  ))     ) _)(   )  )  )
                 ( (  ( \ ) (    (_  ( ) ( )  )   ) )  )) ( )
                  (  (   (  (   (_ ( ) ( _    )  ) (  )  )   )
                 ( (  ( (  (  )     (_  )  ) )  _)   ) _( ( )
                  ((  (   )(    (     _    )   _) _(_ (  (_ )
                   (_((__(_(__(( ( ( |  ) ) ) )_))__))_)___)
                   ((__)        \\||lll|l||///          \_))
                            (   /(/ (  )  ) )\   )
                          (    ( ( ( | | ) ) )\   )
                           (   /(| / ( )) ) ) )) )
                         (     ( ((((_(|)_)))))     )
                          (      ||\(|(|)|/||     )
                        (        |(||(||)||||        )
                          (     //|/l|||)|\\ \     )
                        (/ / //  /|//||||\\  \ \  \ _)
-------------------------------------------------------------------------------
Node: c6059861e1e9
-------------------------------------------------------------------------------
Traceback (most recent call last):
  File "/usr/local/lib/python3.7/site-packages/pendulum/parsing/parser.py", line 352, in _parse
    yearfirst=self._options['year_first']
  File "/usr/local/lib/python3.7/site-packages/dateutil/parser/_parser.py", line 1374, in parse
    return DEFAULTPARSER.parse(timestr, **kwargs)
  File "/usr/local/lib/python3.7/site-packages/dateutil/parser/_parser.py", line 649, in parse
    raise ParserError("Unknown string format: %s", timestr)
dateutil.parser._parser.ParserError: Unknown string format: Invalid date
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
  File "/usr/local/lib/python3.7/site-packages/flask/app.py", line 2447, in wsgi_app
    response = self.full_dispatch_request()
  File "/usr/local/lib/python3.7/site-packages/flask/app.py", line 1952, in full_dispatch_request
    rv = self.handle_user_exception(e)
  File "/usr/local/lib/python3.7/site-packages/flask/app.py", line 1821, in handle_user_exception
    reraise(exc_type, exc_value, tb)
  File "/usr/local/lib/python3.7/site-packages/flask/_compat.py", line 39, in reraise
    raise value
  File "/usr/local/lib/python3.7/site-packages/flask/app.py", line 1950, in full_dispatch_request
    rv = self.dispatch_request()
  File "/usr/local/lib/python3.7/site-packages/flask/app.py", line 1936, in dispatch_request
    return self.view_functions[rule.endpoint](**req.view_args)
  File "/usr/local/lib/python3.7/site-packages/airflow/www_rbac/decorators.py", line 121, in wrapper
    return f(self, *args, **kwargs)
  File "/usr/local/lib/python3.7/site-packages/flask_appbuilder/security/decorators.py", line 109, in wraps
    return f(self, *args, **kwargs)
  File "/usr/local/lib/python3.7/site-packages/airflow/www_rbac/decorators.py", line 92, in view_func
    return f(*args, **kwargs)
  File "/usr/local/lib/python3.7/site-packages/airflow/www_rbac/decorators.py", line 56, in wrapper
    return f(*args, **kwargs)
  File "/usr/local/lib/python3.7/site-packages/airflow/utils/db.py", line 74, in wrapper
    return func(*args, **kwargs)
  File "/usr/local/lib/python3.7/site-packages/airflow/www_rbac/views.py", line 1406, in tree
    base_date = timezone.parse(base_date)
  File "/usr/local/lib/python3.7/site-packages/airflow/utils/timezone.py", line 180, in parse
    return pendulum.parse(string, tz=timezone or TIMEZONE)
  File "/usr/local/lib/python3.7/site-packages/pendulum/parser.py", line 75, in parse
    return Parser(**options).parse(text)
  File "/usr/local/lib/python3.7/site-packages/pendulum/parser.py", line 31, in parse
    parsed = super(Parser, self).parse(text)
  File "/usr/local/lib/python3.7/site-packages/pendulum/parsing/parser.py", line 297, in parse
    return self.normalize(self._parse(text))
  File "/usr/local/lib/python3.7/site-packages/pendulum/parsing/parser.py", line 355, in _parse
    raise ParserError('Invalid date string: {}'.format(text))
pendulum.parsing.exceptions.ParserError: Invalid date string: Invalid date
```

**What you expected to happen**:

User should not receive exception pendulum.parsing.exceptions.ParserError: Invalid date string: Invalid date 

**How to reproduce it**:

1. Create a sample dag/example dag with shorter scheduled interval 
2. Have 2-3 dag runs.
3. Navigate to tree view and click on `Go `button
4. Keep clicking on Go button till there are no dag runs remaining, you would get  `pendulum.parsing.exceptions.ParserError: Invalid date string: Invalid date `exception.

**Screenshot**:

![image](https://user-images.githubusercontent.com/60378152/93097661-78904d80-f6c3-11ea-8616-bf7ac5b17293.png)

![image](https://user-images.githubusercontent.com/60378152/93097682-81811f00-f6c3-11ea-941d-0e82e71f198a.png)

![image](https://user-images.githubusercontent.com/60378152/93097729-8e9e0e00-f6c3-11ea-884c-8ec6ba363fa4.png)


How often does this problem occur? Once? Every time etc?

Everytime




Discussion:
Thanks for opening your first issue here! Be sure to follow the issue template!

Hi, I would like to work on this one. I see that this might be fixed by validating the input, and I assume, preferably on the front-end?
@alxdembo assigned to you

Resolving patch:
diff --git a/airflow/models/dag.py b/airflow/models/dag.py
--- a/airflow/models/dag.py
+++ b/airflow/models/dag.py
@@ -661,6 +661,15 @@ def get_last_dagrun(self, session=None, include_externally_triggered=False):
             self.dag_id, session=session, include_externally_triggered=include_externally_triggered
         )
 
+    @provide_session
+    def has_dag_runs(self, session=None, include_externally_triggered=True) -> bool:
+        return (
+            get_last_dagrun(
+                self.dag_id, session=session, include_externally_triggered=include_externally_triggered
+            )
+            is not None
+        )
+
     @property
     def dag_id(self) -> str:
         return self._dag_id
diff --git a/airflow/www/views.py b/airflow/www/views.py
--- a/airflow/www/views.py
+++ b/airflow/www/views.py
@@ -1981,7 +1981,12 @@ def recurse_nodes(task, visited):
             'instances': [dag_runs.get(d) or {'execution_date': d.isoformat()} for d in dates],
         }
 
-        form = DateTimeWithNumRunsForm(data={'base_date': max_date, 'num_runs': num_runs})
+        form = DateTimeWithNumRunsForm(
+            data={
+                'base_date': max_date or timezone.utcnow(),
+                'num_runs': num_runs,
+            }
+        )
 
         doc_md = wwwutils.wrapped_markdown(getattr(dag, 'doc_md', None), css_class='dag-doc')
 
@@ -2204,7 +2209,12 @@ def duration(self, session=None):
 
         session.commit()
 
-        form = DateTimeWithNumRunsForm(data={'base_date': max_date, 'num_runs': num_runs})
+        form = DateTimeWithNumRunsForm(
+            data={
+                'base_date': max_date or timezone.utcnow(),
+                'num_runs': num_runs,
+            }
+        )
         chart.buildcontent()
         cum_chart.buildcontent()
         s_index = cum_chart.htmlcontent.rfind('});')
@@ -2278,7 +2288,12 @@ def tries(self, session=None):
 
         session.commit()
 
-        form = DateTimeWithNumRunsForm(data={'base_date': max_date, 'num_runs': num_runs})
+        form = DateTimeWithNumRunsForm(
+            data={
+                'base_date': max_date or timezone.utcnow(),
+                'num_runs': num_runs,
+            }
+        )
 
         chart.buildcontent()
 
@@ -2360,7 +2375,12 @@ def landing_times(self, session=None):
 
         session.commit()
 
-        form = DateTimeWithNumRunsForm(data={'base_date': max_date, 'num_runs': num_runs})
+        form = DateTimeWithNumRunsForm(
+            data={
+                'base_date': max_date or timezone.utcnow(),
+                'num_runs': num_runs,
+            }
+        )
         chart.buildcontent()
         return self.render_template(
             'airflow/chart.html',


Test patch: