Repository: dbt-labs/dbt-core

Issue:
[CT-159] [Bug] Partial parsing fails to find new doc block after `git pull`
### Is there an existing issue for this?

- [X] I have searched the existing issues

### Current Behavior

After pulling changes from a remote git branch that include new docblocks, I get an error on compile/run:

```
Running with dbt=0.20.2
Partial parsing enabled: 0 files deleted, 15 files added, 13 files changed.
Encountered an error:
Compilation Error in model source_facebook_ads__videos (models/sources/facebook_ads/source_facebook_ads__videos.sql)
  Documentation for 'model.project_name.source_facebook_ads__videos' depends on doc 'facebook_ads__campaign_id'  which was not found
```

If I run the same command with `--no-partial parse`, the error disappears and subsequent runs are also fine.

### Expected Behavior

Being able to run without disabling partial parse

### Steps To Reproduce

I don't know if these are exact repro steps, but both time I've encountered this it's been:

1. A colleague makes a change on an existing branch that includes doc block changes
2. I pull that branch locally from remote
3. When I try and `dbt compile` I hit the error.

### Relevant log output

```shell
2021-10-27 16:02:54.206833 (MainThread): Traceback (most recent call last):
  File "/Users/josh.devlin/.dbt/versions/0.20.2/lib/python3.8/site-packages/dbt/main.py", line 125, in main
    results, succeeded = handle_and_check(args)
  File "/Users/josh.devlin/.dbt/versions/0.20.2/lib/python3.8/site-packages/dbt/main.py", line 203, in handle_and_check
    task, res = run_from_args(parsed)
  File "/Users/josh.devlin/.dbt/versions/0.20.2/lib/python3.8/site-packages/dbt/main.py", line 256, in run_from_args
    results = task.run()
  File "/Users/josh.devlin/.dbt/versions/0.20.2/lib/python3.8/site-packages/dbt/task/runnable.py", line 406, in run
    self._runtime_initialize()
  File "/Users/josh.devlin/.dbt/versions/0.20.2/lib/python3.8/site-packages/dbt/task/runnable.py", line 122, in _runtime_initialize
    super()._runtime_initialize()
  File "/Users/josh.devlin/.dbt/versions/0.20.2/lib/python3.8/site-packages/dbt/task/runnable.py", line 79, in _runtime_initialize
    self.load_manifest()
  File "/Users/josh.devlin/.dbt/versions/0.20.2/lib/python3.8/site-packages/dbt/task/runnable.py", line 66, in load_manifest
    self.manifest = ManifestLoader.get_full_manifest(self.config)
  File "/Users/josh.devlin/.dbt/versions/0.20.2/lib/python3.8/site-packages/dbt/parser/manifest.py", line 182, in get_full_manifest
    manifest = loader.load()
  File "/Users/josh.devlin/.dbt/versions/0.20.2/lib/python3.8/site-packages/dbt/parser/manifest.py", line 353, in load
    self.process_docs(self.root_project)
  File "/Users/josh.devlin/.dbt/versions/0.20.2/lib/python3.8/site-packages/dbt/parser/manifest.py", line 732, in process_docs
    _process_docs_for_node(ctx, node)
  File "/Users/josh.devlin/.dbt/versions/0.20.2/lib/python3.8/site-packages/dbt/parser/manifest.py", line 916, in _process_docs_for_node
    column.description = get_rendered(column.description, context)
  File "/Users/josh.devlin/.dbt/versions/0.20.2/lib/python3.8/site-packages/dbt/clients/jinja.py", line 598, in get_rendered
    return render_template(template, ctx, node)
  File "/Users/josh.devlin/.dbt/versions/0.20.2/lib/python3.8/site-packages/dbt/clients/jinja.py", line 549, in render_template
    return template.render(ctx)
  File "/Users/josh.devlin/.dbt/versions/0.20.2/lib/python3.8/site-packages/jinja2/environment.py", line 1090, in render
    self.environment.handle_exception()
  File "/Users/josh.devlin/.dbt/versions/0.20.2/lib/python3.8/site-packages/jinja2/environment.py", line 832, in handle_exception
    reraise(*rewrite_traceback_stack(source=source))
  File "/Users/josh.devlin/.dbt/versions/0.20.2/lib/python3.8/site-packages/jinja2/_compat.py", line 28, in reraise
    raise value.with_traceback(tb)
  File "<template>", line 1, in top-level template code
  File "/Users/josh.devlin/.dbt/versions/0.20.2/lib/python3.8/site-packages/jinja2/sandbox.py", line 462, in call
    return __context.call(__obj, *args, **kwargs)
  File "/Users/josh.devlin/.dbt/versions/0.20.2/lib/python3.8/site-packages/dbt/context/docs.py", line 73, in doc
    doc_target_not_found(self.node, doc_name, doc_package_name)
  File "/Users/josh.devlin/.dbt/versions/0.20.2/lib/python3.8/site-packages/dbt/exceptions.py", line 524, in doc_target_not_found
    raise_compiler_error(msg, model)
  File "/Users/josh.devlin/.dbt/versions/0.20.2/lib/python3.8/site-packages/dbt/exceptions.py", line 444, in raise_compiler_error
    raise CompilationException(msg, node)
dbt.exceptions.CompilationException: Compilation Error in model source_facebook_ads__conversions (models/sources/facebook_ads/source_facebook_ads__conversions.sql)
  Documentation for 'model.project-name.source_facebook_ads__conversions' depends on doc 'facebook_ads__campaign_id'  which was not found
```


### Environment

```markdown
- OS: Mac OS 11.6
- Python: 3.8.10
- dbt: 0.20.2
```


### What database are you using dbt with?

snowflake

### Additional Context

_No response_


Discussion:
Thanks for the report @jaypeedevlin! I'll see if I can pull together a simple reproduction case.

It seems like we you all the way through parsing successfully, but dbt missed adding a `docs` block to the manifest, such that you trip `doc_target_not_found` later on. Unfortunately, the way we implemented exception-catching for partial parsing in v0.20.2 won't be able to help there.

Just wondering, are you able to tell if the model + doc block in question in totally new files, or edited files? (I see the branch included 15 files added, 13 files changed)
+1 dbt Cloud user 

Context of how this came up:

> I had to make a GIT operations outside of dbt, so I ended up pulling the revert of the revert
> This resulted in the following compilation message:
> 
> "Compilation Error in model <model name> (<model path>)
>   Documentation for 'model.<model name>' depends on doc '<model name>'  which was not found"
>
> The doc file was nicely in place but I suspect that the incremental parsing was causing it to not be seen.
> I have just added a space and saved the file to fix the issue
I also encountered this error with DBT 1.0.4 after a simple branch change.  

I was working on my feature branch where I added a file `docs_blocks.md` with docs block `my_doc`. `dbt compile/run/docs` were working fine with the documentation showing up as expected.

Then I came back to my main branch, which doesn't have the file with docs blocks, to execute some runs that also worked as expected.

But when I came back to my feature branch, the `dbt compile` failed with:
```
Compilation Error in model my_model (models\my_model.sql)
  Documentation for 'model.my_model' depends on doc 'my_doc'  which was not found
```

So it looks like DBT didn't notice that the branch change reintroduced the file `docs_blocks.md`, it didn't bother to add it in the compilation and thus the compilation of `my_schema.yml` which describes `my_model` failed.

My workaround was to add a small change in `docs_blocks.md`, then  `dbt compile` worked as expected.

Environment:
- dbt 1.0.4
- Python 3.8.10
- mintty 3.5.2 (Git Bash for Windows)
- Windows  10 Pro
@jtcohen6 hope this helps, although it seems like my steps to reproduce this are very similar to that of the original issue. 

1. On a local git branch add a doc x for model y and define it as the models description within your models yml
2. Push branch to remote, create a pr and merge to master 
3. Pull to local master branch and create a new branch to work on 
4. Try running a model with `dbt run --models `
5. You should see the following error  'Documentation for model y depends on doc x which was not found'
I just wanted to add in that I still see this issue come up every few weeks when I'm working across multiple branches.
I also want to add in that I'm still seeing this issue come up, hope DBT could help to fix this. 
Huzzah! Following @alvare16's instructions, I was able to reliably reproduce this error:
```sql
-- models/my_model.sql
select 1 as id
```
```yml
# my_model.yml
version: 2
models:
  - name: my_model
    columns:
      - name: id
        description: "{{ doc('whatever') }}"
```
```md
<!--- my_model.md --->
{% docs whatever %}
  cool stuff
{% enddocs %}
```
Steps:
1. `dbt parse` (successful)
2. Remove the docs block + description
3. `dbt parse` (successful)
4. Add back the docs block + description
5. `dbt parse` - fails with
```
04:02:21  Encountered an error:
Compilation Error in model my_model (models/my_model.sql)
  Documentation for 'model.testy.my_model' depends on doc 'whatever'  which was not found
```

I've only been able to do a little spelunking on this. So far my hunch is that the documentation file ID seems to stick around in the partial parsing (previous save) manifest, even after the file is removed. This causes the file to be re-parsed in the manifest, with no contents, but its file ID is still tracked, so it isn't slated for addition when it's actually added back.

Resolving patch:
diff --git a/core/dbt/parser/partial.py b/core/dbt/parser/partial.py
--- a/core/dbt/parser/partial.py
+++ b/core/dbt/parser/partial.py
@@ -573,6 +573,10 @@ def delete_doc_node(self, source_file):
         # doc source_file.nodes
         self.schedule_nodes_for_parsing(source_file.nodes)
         source_file.nodes = []
+        # Remove the file object
+        self.deleted_manifest.files[source_file.file_id] = self.saved_manifest.files.pop(
+            source_file.file_id
+        )
 
     # Schema files -----------------------
     # Changed schema files


Test patch:
diff --git a/test/integration/068_partial_parsing_tests/test-files/my_macro-docs.sql b/test/integration/068_partial_parsing_tests/test-files/my_macro-docs.sql
deleted file mode 100644
--- a/test/integration/068_partial_parsing_tests/test-files/my_macro-docs.sql
+++ /dev/null
@@ -1,6 +0,0 @@
-{% macro my_macro(something) %}
-
-    select
-        '{{ something }}' as something2
-
-{% endmacro %}
diff --git a/test/integration/068_partial_parsing_tests/test-files/schema-docs.yml b/test/integration/068_partial_parsing_tests/test-files/schema-docs.yml
deleted file mode 100644
--- a/test/integration/068_partial_parsing_tests/test-files/schema-docs.yml
+++ /dev/null
@@ -1,5 +0,0 @@
-version: 2
-
-models:
-    - name: model_one
-      description: "{{ doc('customer_table') }}"
diff --git a/test/integration/068_partial_parsing_tests/test-files/schema-docs2.yml b/test/integration/068_partial_parsing_tests/test-files/schema-docs2.yml
deleted file mode 100644
--- a/test/integration/068_partial_parsing_tests/test-files/schema-docs2.yml
+++ /dev/null
@@ -1,38 +0,0 @@
-version: 2
-
-models:
-    - name: model_one
-      description: "{{ doc('customer_table') }}"
-
-macros:
-    - name: my_macro
-      description: "{{ doc('customer_table') }}"
-
-sources:
-  - name: seed_sources
-    description: "{{ doc('customer_table') }}"
-    schema: "{{ target.schema }}"
-    tables:
-      - name: raw_customers
-        columns:
-          - name: id
-            tests:
-              - not_null:
-                  severity: "{{ 'error' if target.name == 'prod' else 'warn' }}"
-              - unique
-          - name: first_name
-          - name: last_name
-          - name: email
-
-exposures:
-  - name: proxy_for_dashboard
-    description: "{{ doc('customer_table') }}"
-    type: "dashboard"
-    owner:
-      name: "Dashboard Tester"
-      email: "tester@dashboard.com"
-    depends_on:
-      - ref("model_one")
-      - ref("raw_customers")
-      - source("seed_sources", "raw_customers")
-
diff --git a/test/integration/068_partial_parsing_tests/test_pp_docs.py b/test/integration/068_partial_parsing_tests/test_pp_docs.py
deleted file mode 100644
--- a/test/integration/068_partial_parsing_tests/test_pp_docs.py
+++ /dev/null
@@ -1,121 +0,0 @@
-from dbt.exceptions import CompilationException
-from dbt.contracts.graph.manifest import Manifest
-from dbt.contracts.files import ParseFileType
-from test.integration.base import DBTIntegrationTest, use_profile, normalize, get_manifest
-import shutil
-import os
-
-
-class TestDocs(DBTIntegrationTest):
-
-    @property
-    def schema(self):
-        return "test_068docs"
-
-    @property
-    def models(self):
-        return "models"
-
-    @property
-    def project_config(self):
-        return {
-            'config-version': 2,
-            'seed-paths': ['seeds'],
-            'test-paths': ['tests'],
-            'macro-paths': ['macros'],
-            'analysis-paths': ['analyses'],
-            'snapshot-paths': ['snapshots'],
-            'seeds': {
-                'quote_columns': False,
-            },
-        }
-
-    def setup_directories(self):
-        os.mkdir(os.path.join(self.test_root_dir, 'models'))
-        os.mkdir(os.path.join(self.test_root_dir, 'tests'))
-        os.mkdir(os.path.join(self.test_root_dir, 'seeds'))
-        os.mkdir(os.path.join(self.test_root_dir, 'macros'))
-        os.mkdir(os.path.join(self.test_root_dir, 'analyses'))
-        os.mkdir(os.path.join(self.test_root_dir, 'snapshots'))
-        os.environ['DBT_PP_TEST'] = 'true'
-
-
-    @use_profile('postgres')
-    def test_postgres_pp_docs(self):
-        # initial run
-        self.setup_directories()
-        self.copy_file('test-files/model_one.sql', 'models/model_one.sql')
-        self.copy_file('test-files/raw_customers.csv', 'seeds/raw_customers.csv')
-        self.copy_file('test-files/my_macro-docs.sql', 'macros/my_macro.sql')
-        results = self.run_dbt(["run"])
-        self.assertEqual(len(results), 1)
-
-        # Add docs file customers.md
-        self.copy_file('test-files/customers1.md', 'models/customers.md')
-        results = self.run_dbt(["--partial-parse", "run"])
-        manifest = get_manifest()
-        self.assertEqual(len(manifest.docs), 2)
-        model_one_node = manifest.nodes['model.test.model_one']
-
-        # Add schema file with 'docs' description
-        self.copy_file('test-files/schema-docs.yml', 'models/schema.yml')
-        results = self.run_dbt(["--partial-parse", "run"])
-        manifest = get_manifest()
-        self.assertEqual(len(manifest.docs), 2)
-        doc_id = 'test.customer_table'
-        self.assertIn(doc_id, manifest.docs)
-        doc = manifest.docs[doc_id]
-        doc_file_id = doc.file_id
-        self.assertIn(doc_file_id, manifest.files)
-        source_file = manifest.files[doc_file_id]
-        self.assertEqual(len(source_file.nodes), 1)
-        model_one_id = 'model.test.model_one'
-        self.assertIn(model_one_id, source_file.nodes)
-        model_node = manifest.nodes[model_one_id]
-        self.assertEqual(model_node.description, 'This table contains customer data')
-
-        # Update the doc file
-        self.copy_file('test-files/customers2.md', 'models/customers.md')
-        results = self.run_dbt(["--partial-parse", "run"])
-        manifest = get_manifest()
-        self.assertEqual(len(manifest.docs), 2)
-        doc_node = manifest.docs[doc_id]
-        model_one_id = 'model.test.model_one'
-        self.assertIn(model_one_id, manifest.nodes)
-        model_node = manifest.nodes[model_one_id]
-        self.assertRegex(model_node.description, r'LOTS')
-
-        # Add a macro patch, source and exposure with doc
-        self.copy_file('test-files/schema-docs2.yml', 'models/schema.yml')
-        results = self.run_dbt(["--partial-parse", "run"])
-        self.assertEqual(len(results), 1)
-        manifest = get_manifest()
-        doc_file = manifest.files[doc_file_id]
-        expected_nodes = ['model.test.model_one', 'source.test.seed_sources.raw_customers', 'macro.test.my_macro', 'exposure.test.proxy_for_dashboard']
-        self.assertEqual(expected_nodes, doc_file.nodes)
-        source_id = 'source.test.seed_sources.raw_customers'
-        self.assertEqual(manifest.sources[source_id].source_description, 'LOTS of customer data')
-        macro_id = 'macro.test.my_macro'
-        self.assertEqual(manifest.macros[macro_id].description, 'LOTS of customer data')
-        exposure_id = 'exposure.test.proxy_for_dashboard'
-        self.assertEqual(manifest.exposures[exposure_id].description, 'LOTS of customer data')
-
-
-        # update the doc file again
-        self.copy_file('test-files/customers1.md', 'models/customers.md')
-        results = self.run_dbt(["--partial-parse", "run"])
-        manifest = get_manifest()
-        source_file = manifest.files[doc_file_id]
-        model_one_id = 'model.test.model_one'
-        self.assertIn(model_one_id, source_file.nodes)
-        model_node = manifest.nodes[model_one_id]
-        self.assertEqual(model_node.description, 'This table contains customer data')
-        self.assertEqual(manifest.sources[source_id].source_description, 'This table contains customer data')
-        self.assertEqual(manifest.macros[macro_id].description, 'This table contains customer data')
-        self.assertEqual(manifest.exposures[exposure_id].description, 'This table contains customer data')
-
-        # check that _lock is working
-        with manifest._lock:
-            self.assertIsNotNone(manifest._lock)
-
-
diff --git a/tests/functional/partial_parsing/test_pp_docs.py b/tests/functional/partial_parsing/test_pp_docs.py
new file mode 100644
--- /dev/null
+++ b/tests/functional/partial_parsing/test_pp_docs.py
@@ -0,0 +1,256 @@
+import pytest
+from dbt.tests.util import run_dbt, write_file, get_manifest, rm_file
+
+model_one_sql = """
+select 1 as fun
+"""
+
+raw_customers_csv = """id,first_name,last_name,email
+1,Michael,Perez,mperez0@chronoengine.com
+2,Shawn,Mccoy,smccoy1@reddit.com
+3,Kathleen,Payne,kpayne2@cargocollective.com
+4,Jimmy,Cooper,jcooper3@cargocollective.com
+5,Katherine,Rice,krice4@typepad.com
+6,Sarah,Ryan,sryan5@gnu.org
+7,Martin,Mcdonald,mmcdonald6@opera.com
+8,Frank,Robinson,frobinson7@wunderground.com
+9,Jennifer,Franklin,jfranklin8@mail.ru
+10,Henry,Welch,hwelch9@list-manage.com
+"""
+
+my_macro_sql = """
+{% macro my_macro(something) %}
+
+    select
+        '{{ something }}' as something2
+
+{% endmacro %}
+
+"""
+
+customers1_md = """
+{% docs customer_table %}
+
+This table contains customer data
+
+{% enddocs %}
+"""
+
+customers2_md = """
+{% docs customer_table %}
+
+LOTS of customer data
+
+{% enddocs %}
+
+"""
+
+schema1_yml = """
+version: 2
+
+models:
+    - name: model_one
+      description: "{{ doc('customer_table') }}"
+"""
+
+schema2_yml = """
+version: 2
+
+models:
+    - name: model_one
+      description: "{{ doc('customer_table') }}"
+
+macros:
+    - name: my_macro
+      description: "{{ doc('customer_table') }}"
+
+sources:
+  - name: seed_sources
+    description: "{{ doc('customer_table') }}"
+    schema: "{{ target.schema }}"
+    tables:
+      - name: raw_customers
+        columns:
+          - name: id
+            tests:
+              - not_null:
+                  severity: "{{ 'error' if target.name == 'prod' else 'warn' }}"
+              - unique
+          - name: first_name
+          - name: last_name
+          - name: email
+
+exposures:
+  - name: proxy_for_dashboard
+    description: "{{ doc('customer_table') }}"
+    type: "dashboard"
+    owner:
+      name: "Dashboard Tester"
+      email: "tester@dashboard.com"
+    depends_on:
+      - ref("model_one")
+      - ref("raw_customers")
+      - source("seed_sources", "raw_customers")
+"""
+
+
+class TestDocs:
+    @pytest.fixture(scope="class")
+    def models(self):
+        return {
+            "model_one.sql": model_one_sql,
+        }
+
+    @pytest.fixture(scope="class")
+    def seeds(self):
+        return {
+            "raw_customers.csv": raw_customers_csv,
+        }
+
+    @pytest.fixture(scope="class")
+    def macros(self):
+        return {
+            "my_macro.sql": my_macro_sql,
+        }
+
+    def test_pp_docs(self, project):
+        run_dbt(["seed"])
+        results = run_dbt(["run"])
+        assert len(results) == 1
+
+        # Add docs file customers.md
+        write_file(customers1_md, project.project_root, "models", "customers.md")
+        results = run_dbt(["--partial-parse", "run"])
+        manifest = get_manifest(project.project_root)
+        assert len(manifest.docs) == 2
+
+        # Add schema file with 'docs' description
+        write_file(schema1_yml, project.project_root, "models", "schema.yml")
+        results = run_dbt(["--partial-parse", "run"])
+        manifest = get_manifest(project.project_root)
+        assert len(manifest.docs) == 2
+        doc_id = "test.customer_table"
+        assert doc_id in manifest.docs
+        doc = manifest.docs[doc_id]
+        doc_file_id = doc.file_id
+        assert doc_file_id in manifest.files
+        source_file = manifest.files[doc_file_id]
+        assert len(source_file.nodes) == 1
+        model_one_id = "model.test.model_one"
+        assert model_one_id in source_file.nodes
+        model_node = manifest.nodes[model_one_id]
+        assert model_node.description == "This table contains customer data"
+
+        # Update the doc file
+        write_file(customers2_md, project.project_root, "models", "customers.md")
+        results = run_dbt(["--partial-parse", "run"])
+        manifest = get_manifest(project.project_root)
+        assert len(manifest.docs) == 2
+        assert model_one_id in manifest.nodes
+        model_node = manifest.nodes[model_one_id]
+        assert "LOTS" in model_node.description
+
+        # Add a macro patch, source and exposure with doc
+        write_file(schema2_yml, project.project_root, "models", "schema.yml")
+        results = run_dbt(["--partial-parse", "run"])
+        assert len(results) == 1
+        manifest = get_manifest(project.project_root)
+        doc_file = manifest.files[doc_file_id]
+        expected_nodes = [
+            "model.test.model_one",
+            "source.test.seed_sources.raw_customers",
+            "macro.test.my_macro",
+            "exposure.test.proxy_for_dashboard",
+        ]
+        assert expected_nodes == doc_file.nodes
+        source_id = "source.test.seed_sources.raw_customers"
+        assert manifest.sources[source_id].source_description == "LOTS of customer data"
+        macro_id = "macro.test.my_macro"
+        assert manifest.macros[macro_id].description == "LOTS of customer data"
+        exposure_id = "exposure.test.proxy_for_dashboard"
+        assert manifest.exposures[exposure_id].description == "LOTS of customer data"
+
+        # update the doc file again
+        write_file(customers1_md, project.project_root, "models", "customers.md")
+        results = run_dbt(["--partial-parse", "run"])
+        manifest = get_manifest(project.project_root)
+        source_file = manifest.files[doc_file_id]
+        assert model_one_id in source_file.nodes
+        model_node = manifest.nodes[model_one_id]
+        assert model_node.description == "This table contains customer data"
+        assert (
+            manifest.sources[source_id].source_description == "This table contains customer data"
+        )
+        assert manifest.macros[macro_id].description == "This table contains customer data"
+        assert manifest.exposures[exposure_id].description == "This table contains customer data"
+
+        # check that _lock is working
+        with manifest._lock:
+            assert manifest._lock
+
+
+my_model_yml = """
+version: 2
+models:
+  - name: my_model
+    columns:
+      - name: id
+        description: "{{ doc('whatever') }}"
+"""
+
+my_model_no_description_yml = """
+version: 2
+models:
+  - name: my_model
+    columns:
+      - name: id
+"""
+
+my_model_md = """
+{% docs whatever %}
+  cool stuff
+{% enddocs %}
+"""
+
+
+class TestDocsRemoveReplace:
+    @pytest.fixture(scope="class")
+    def models(self):
+        return {
+            "my_model.sql": "select 1 as id",
+            "my_model.yml": my_model_yml,
+            "my_model.md": my_model_md,
+        }
+
+    def test_remove_replace(self, project):
+        run_dbt(["parse", "--write-manifest"])
+        manifest = get_manifest(project.project_root)
+        doc_id = "test.whatever"
+        assert doc_id in manifest.docs
+        doc = manifest.docs[doc_id]
+        doc_file = manifest.files[doc.file_id]
+
+        model_id = "model.test.my_model"
+        assert model_id in manifest.nodes
+
+        assert doc_file.nodes == [model_id]
+
+        model = manifest.nodes[model_id]
+        model_file_id = model.file_id
+        assert model_file_id in manifest.files
+
+        # remove the doc file
+        rm_file(project.project_root, "models", "my_model.md")
+        # remove description from schema file
+        write_file(my_model_no_description_yml, project.project_root, "models", "my_model.yml")
+        run_dbt(["parse", "--write-manifest"])
+        manifest = get_manifest(project.project_root)
+        assert doc_id not in manifest.docs
+        # The bug was that the file still existed in manifest.files
+        assert doc.file_id not in manifest.files
+
+        # put back the doc file
+        write_file(my_model_md, project.project_root, "models", "my_model.md")
+        # put back the description in the schema file
+        write_file(my_model_yml, project.project_root, "models", "my_model.yml")
+        run_dbt(["parse"])