diff --git a/news/91.bugfix.rst b/news/91.bugfix.rst
new file mode 100644
index 0000000..6b16158
--- /dev/null
+++ b/news/91.bugfix.rst
@@ -0,0 +1,6 @@
+Some valid states that were previously rejected are now accepted. This affects
+states where multiple candidates for the same dependency conflict with each
+other. The ``information`` argument passed to
+``AbstractProvider.get_preference`` may now contain empty iterators. This has
+always been allowed by the method definition but it was previously not possible
+in practice.
diff --git a/src/resolvelib/resolvers.py b/src/resolvelib/resolvers.py
index be8d5f0..49e30c7 100644
--- a/src/resolvelib/resolvers.py
+++ b/src/resolvelib/resolvers.py
@@ -173,6 +173,31 @@ class Resolution(object):
             raise RequirementsConflicted(criterion)
         criteria[identifier] = criterion
 
+    def _remove_information_from_criteria(self, criteria, parents):
+        """Remove information from parents of criteria.
+
+        Concretely, removes all values from each criterion's ``information``
+        field that have one of ``parents`` as provider of the requirement.
+
+        :param criteria: The criteria to update.
+        :param parents: Identifiers for which to remove information from all criteria.
+        """
+        if not parents:
+            return
+        for key, criterion in criteria.items():
+            criteria[key] = Criterion(
+                criterion.candidates,
+                [
+                    information
+                    for information in criterion.information
+                    if (
+                        information[1] is None
+                        or self._p.identify(information[1]) not in parents
+                    )
+                ],
+                criterion.incompatibilities,
+            )
+
     def _get_preference(self, name):
         return self._p.get_preference(
             identifier=name,
@@ -367,6 +392,11 @@ class Resolution(object):
                 self._r.ending(state=self.state)
                 return self.state
 
+            # keep track of satisfied names to calculate diff after pinning
+            satisfied_names = set(self.state.criteria.keys()) - set(
+                unsatisfied_names
+            )
+
             # Choose the most preferred unpinned criterion to try.
             name = min(unsatisfied_names, key=self._get_preference)
             failure_causes = self._attempt_to_pin_criterion(name)
@@ -383,6 +413,17 @@ class Resolution(object):
                 if not success:
                     raise ResolutionImpossible(self.state.backtrack_causes)
             else:
+                # discard as information sources any invalidated names
+                # (unsatisfied names that were previously satisfied)
+                newly_unsatisfied_names = {
+                    key
+                    for key, criterion in self.state.criteria.items()
+                    if key in satisfied_names
+                    and not self._is_current_pin_satisfying(key, criterion)
+                }
+                self._remove_information_from_criteria(
+                    self.state.criteria, newly_unsatisfied_names
+                )
                 # Pinning was successful. Push a new state to do another pin.
                 self._push_new_state()
 
diff --git a/src/resolvelib/resolvers.pyi b/src/resolvelib/resolvers.pyi
index 0eb5b21..528a1a2 100644
--- a/src/resolvelib/resolvers.pyi
+++ b/src/resolvelib/resolvers.pyi
@@ -55,6 +55,18 @@ class ResolutionImpossible(ResolutionError, Generic[RT, CT]):
 class ResolutionTooDeep(ResolutionError):
     round_count: int
 
+# This should be a NamedTuple, but Python 3.6 has a bug that prevents it.
+# https://stackoverflow.com/a/50531189/1376863
+class State(tuple, Generic[RT, CT, KT]):
+    mapping: Mapping[KT, CT]
+    criteria: Mapping[KT, Criterion[RT, CT, KT]]
+    backtrack_causes: Collection[RequirementInformation[RT, CT]]
+
+class Resolution(Generic[RT, CT, KT]):
+    def resolve(
+        self, requirements: Iterable[RT], max_rounds: int
+    ) -> State[RT, CT, KT]: ...
+
 class Result(Generic[RT, CT, KT]):
     mapping: Mapping[KT, CT]
     graph: DirectedGraph[Optional[KT]]

diff --git a/tests/functional/python/inputs/index/same-package.json b/tests/functional/python/inputs/index/same-package.json
index 0090db0..c0fc125 100644
--- a/tests/functional/python/inputs/index/same-package.json
+++ b/tests/functional/python/inputs/index/same-package.json
@@ -2,37 +2,37 @@
 	"package-a": {
 		"0.1.0": {
 			"dependencies": [
-				"package-x=='0.1.0'; extra == 'x'",
-				"package-y=='0.1.0'; extra == 'y'",
-				"package-z=='0.1.0'; extra == 'z'"
+				"package-x==0.1.0; extra == 'x'",
+				"package-y==0.1.0; extra == 'y'",
+				"package-z==0.1.0; extra == 'z'"
 			]
 		},
 		"1.0.0": {
 			"dependencies": [
-				"package-x=='1.0.0'; extra == 'x'",
-				"package-y=='1.0.0'; extra == 'y'",
-				"package-z=='1.0.0'; extra == 'z'"
+				"package-x==1.0.0; extra == 'x'",
+				"package-y==1.0.0; extra == 'y'",
+				"package-z==1.0.0; extra == 'z'"
 			]
 		},
 		"1.1.0": {
 			"dependencies": [
-				"package-x=='1.1.0'; extra == 'x'",
-				"package-y=='1.1.0'; extra == 'y'",
-				"package-z=='1.1.0'; extra == 'z'"
+				"package-x==1.1.0; extra == 'x'",
+				"package-y==1.1.0; extra == 'y'",
+				"package-z==1.1.0; extra == 'z'"
 			]
 		},
 		"1.2.0": {
 			"dependencies": [
-				"package-x=='1.2.0'; extra == 'x'",
-				"package-y=='1.2.0'; extra == 'y'",
-				"package-z=='1.2.0'; extra == 'z'"
+				"package-x==1.2.0; extra == 'x'",
+				"package-y==1.2.0; extra == 'y'",
+				"package-z==1.2.0; extra == 'z'"
 			]
 		},
 		"1.3.0": {
 			"dependencies": [
-				"package-x=='1.3.0'; extra == 'x'",
-				"package-y=='1.3.0'; extra == 'y'",
-				"package-z=='1.3.0'; extra == 'z'"
+				"package-x==1.3.0; extra == 'x'",
+				"package-y==1.3.0; extra == 'y'",
+				"package-z==1.3.0; extra == 'z'"
 			]
 		}
 	},
diff --git a/tests/functional/python/test_resolvers_python.py b/tests/functional/python/test_resolvers_python.py
index 4904615..2b6de36 100644
--- a/tests/functional/python/test_resolvers_python.py
+++ b/tests/functional/python/test_resolvers_python.py
@@ -129,7 +129,6 @@ CASE_NAMES = [name for name in os.listdir(CASE_DIR) if name.endswith(".json")]
 
 XFAIL_CASES = {
     "pyrex-1.9.8.json": "Too many rounds (>500)",
-    "same-package-extras.json": "State not cleaned up correctly",
 }
 
 
diff --git a/tests/test_resolvers.py b/tests/test_resolvers.py
index 8af925a..176108f 100644
--- a/tests/test_resolvers.py
+++ b/tests/test_resolvers.py
@@ -1,4 +1,18 @@
+from typing import (
+    Any,
+    Iterable,
+    Iterator,
+    List,
+    Mapping,
+    Sequence,
+    Set,
+    Tuple,
+    Union,
+)
+
 import pytest
+from packaging.requirements import Requirement
+from packaging.version import Version
 
 from resolvelib import (
     AbstractProvider,
@@ -7,6 +21,12 @@ from resolvelib import (
     ResolutionImpossible,
     Resolver,
 )
+from resolvelib.resolvers import (
+    Criterion,
+    RequirementInformation,
+    RequirementsConflicted,
+    Resolution,
+)
 
 
 def test_candidate_inconsistent_error():
@@ -143,3 +163,109 @@ def test_resolving_conflicts():
     backtracking_causes = run_resolver([("a", {1, 2}), ("b", {1})])
     exception_causes = run_resolver([("a", {2}), ("b", {1})])
     assert exception_causes == backtracking_causes
+
+
+def test_pin_conflict_with_self(monkeypatch, reporter):
+    # type: (Any, BaseReporter) -> None
+    """
+    Verify correct behavior of attempting to pin a candidate version that conflicts
+    with a previously pinned (now invalidated) version for that same candidate (#91).
+    """
+    Candidate = Tuple[
+        str, Version, Sequence[str]
+    ]  # name, version, requirements
+    all_candidates = {
+        "parent": [("parent", Version("1"), ["child<2"])],
+        "child": [
+            ("child", Version("2"), ["grandchild>=2"]),
+            ("child", Version("1"), ["grandchild<2"]),
+            ("child", Version("0.1"), ["grandchild"]),
+        ],
+        "grandchild": [
+            ("grandchild", Version("2"), []),
+            ("grandchild", Version("1"), []),
+        ],
+    }  # type: Mapping[str, Sequence[Candidate]]
+
+    class Provider(AbstractProvider):  # AbstractProvider[str, Candidate, str]
+        def identify(self, requirement_or_candidate):
+            # type: (Union[str, Candidate]) -> str
+            result = (
+                Requirement(requirement_or_candidate).name
+                if isinstance(requirement_or_candidate, str)
+                else requirement_or_candidate[0]
+            )
+            assert result in all_candidates, "unknown requirement_or_candidate"
+            return result
+
+        def get_preference(self, identifier, *args, **kwargs):
+            # type: (str, *object, **object) -> str
+            # prefer child over parent (alphabetically)
+            return identifier
+
+        def get_dependencies(self, candidate):
+            # type: (Candidate) -> Sequence[str]
+            return candidate[2]
+
+        def find_matches(
+            self,
+            identifier,  # type: str
+            requirements,  # type: Mapping[str, Iterator[str]]
+            incompatibilities,  # type: Mapping[str, Iterator[Candidate]]
+        ):
+            # type: (...) -> Iterator[Candidate]
+            return (
+                candidate
+                for candidate in all_candidates[identifier]
+                if all(
+                    self.is_satisfied_by(req, candidate)
+                    for req in requirements[identifier]
+                )
+                if candidate not in incompatibilities[identifier]
+            )
+
+        def is_satisfied_by(self, requirement, candidate):
+            # type: (str, Candidate) -> bool
+            return candidate[1] in Requirement(requirement).specifier
+
+    # patch Resolution._get_updated_criteria to collect rejected states
+    rejected_criteria = []  # type: List[Criterion]
+    get_updated_criteria_orig = (
+        Resolution._get_updated_criteria  # type: ignore[attr-defined]
+    )
+
+    def get_updated_criteria_patch(self, candidate):
+        try:
+            return get_updated_criteria_orig(self, candidate)
+        except RequirementsConflicted as e:
+            rejected_criteria.append(e.criterion)
+            raise
+
+    monkeypatch.setattr(
+        Resolution, "_get_updated_criteria", get_updated_criteria_patch
+    )
+
+    resolver = Resolver(
+        Provider(), reporter
+    )  # type: Resolver[str, Candidate, str]
+    result = resolver.resolve(["child", "parent"])
+
+    def get_child_versions(information):
+        # type: (Iterable[RequirementInformation[str, Candidate]]) -> Set[str]
+        return {
+            str(inf.parent[1])
+            for inf in information
+            if inf.parent is not None and inf.parent[0] == "child"
+        }
+
+    # verify that none of the rejected criteria are based on more than one candidate for
+    # child
+    assert not any(
+        len(get_child_versions(criterion.information)) > 1
+        for criterion in rejected_criteria
+    )
+
+    assert set(result.mapping) == {"parent", "child", "grandchild"}
+    assert result.mapping["parent"][1] == Version("1")
+    assert result.mapping["child"][1] == Version("1")
+    assert result.mapping["grandchild"][1] == Version("1")

criteria compatibility check while backtracking includes dependencies for version we're backtracking on
# Context
While using pip, which makes use of this library, I noticed that `pip install flake8 flake8-isort` resulted in backtracking over all flake8's versions, not finding a suitable candidate, followed by backtracking over flake8-isort's versions until a suitable (but very old) candidate was found there. When I looked at these projects I didn't see any reason why none of the flake8 versions would be compatible with the latest flake8-isort. Indeed, reversing the order (`pip install flake8-isort`) installs the latest flake8-isort and a compatible flake8 as expected, only having to backtrack once on flake8.
When I noticed this seemingly inconsistent behavior I added some breakpoints to this library's code and tried to get a grasp of what was going on. I should note as a disclaimer that I haven't worked with this code before, so I might just be missing something.

# Concrete
Here's a timeline of what I believe happens for `pip install flake8 flake8-isort`:
1. The latest flake8 (`4.0.1` at the time of writing) gets selected.
2. The latest flake8-isort gets selected
3. The flake8-isort candidate requires `flake8<4`, which isn't compatible with the flake8 candidate, so we backtrack on flake8.
4. For each flake8 version, we check if its dependencies are compatible with the current criteria: https://github.com/sarugaku/resolvelib/blob/62c56b587c69a42c8f006e032bbf17a63df54d45/src/resolvelib/resolvers.py#L203-L204
5. This fails with
```
> /home/sander/.virtualenvs/temp/lib/python3.9/site-packages/pip/_vendor/resolvelib/resolvers.py(218)_attempt_to_pin_criterion()
-> continue
(Pdb) l
213  	            try:
214  	                criteria = self._get_updated_criteria(candidate)
215  	            except RequirementsConflicted as e:
216  	                causes.append(e.criterion)
217  	                breakpoint()
218  ->	                continue
219  	
220  	            # Check the newly-pinned candidate actually works. This should
221  	            # always pass under normal circumstances, but in the case of a
222  	            # faulty provider, we will raise an error to notify the implementer
223  	            # to fix find_matches() and/or is_satisfied_by().
(Pdb) e.criterion
Criterion((SpecifierRequirement('pyflakes<2.5.0,>=2.4.0'), via=LinkCandidate('https://files.pythonhosted.org/packages/34/39/cde2c8a227abb4f9ce62fe55586b920f438f1d2903a1a22514d0b982c333/flake8-4.0.1-py2.py3-none-any.whl#sha256=479b1304f72536a55948cb40a32dce8bb0ffe3501e26eaf292c7e60eb5e0428d (from https://pypi.org/simple/flake8/) (requires-python:>=3.6)')), (SpecifierRequirement('pyflakes<2.4.0,>=2.3.0'), via=LinkCandidate('https://files.pythonhosted.org/packages/fc/80/35a0716e5d5101e643404dabd20f07f5528a21f3ef4032d31a49c913237b/flake8-3.9.2-py2.py3-none-any.whl#sha256=bf8fd333346d844f616e8d47905ef3a3384edae6b4e9beb0c5101e25e3110907 (from https://pypi.org/simple/flake8/) (requires-python:!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7)')))
```

This seems to indicate that the dependency compatibility check includes the dependencies of the version we're backtracking on. Since the currently selected flake8 has a dependency constraint which is incompatible with the constraints for all older versions, backtracking fails. In practice, the latest `flake8<4` is compatible with the rest of the current criteria.
tests/test_resolvers.py::test_pin_conflict_with_self
tests/functional/python/test_resolvers_python.py::test_resolver[with-without-extras] tests/functional/python/test_resolvers_python.py::test_resolver[same-package-extras] tests/functional/python/test_resolvers_python.py::test_resolver[cheroot] tests/functional/python/test_resolvers_python.py::test_resolver[different-extras] tests/functional/python/test_resolvers_python.py::test_resolver[conflict-with-dependency] tests/functional/python/test_resolvers_python.py::test_resolver[same-package] tests/functional/python/test_resolvers_python.py::test_resolver[chalice] tests/test_resolvers.py::test_candidate_inconsistent_error tests/test_resolvers.py::test_candidate_depends_on_requirements_of_same_identifier[specifiers0] tests/test_resolvers.py::test_candidate_depends_on_requirements_of_same_identifier[specifiers1] tests/test_resolvers.py::test_resolving_conflicts