diff --git a/hxl/validation.py b/hxl/validation.py
index 3eb1eba..922bfaf 100644
--- a/hxl/validation.py
+++ b/hxl/validation.py
@@ -1508,11 +1508,6 @@ def validate(data, schema=None):
 
     issue_map = dict()
 
-    def make_rule_hash(rule):
-        """Make a good-enough hash for a rule."""
-        s = "\r".join([str(rule.severity), str(rule.description), str(rule.tag_pattern)])
-        return base64.urlsafe_b64encode(hashlib.md5(s.encode('utf-8')).digest())[:8].decode('ascii')
-
     def add_issue(issue):
         hash = make_rule_hash(issue.rule)
         issue_map.setdefault(hash, []).append(issue)
@@ -1562,9 +1557,10 @@ def make_json_report(status, issue_map, schema_url=None, data_url=None):
 
     # add the issue objects
     for rule_id, locations in issue_map.items():
-        json_report['stats']['total'] += len(locations)
-        json_report['stats'][locations[0].rule.severity] += len(locations)
-        json_report['issues'].append(make_json_issue(rule_id, locations))
+        json_issue = make_json_issue(rule_id, locations)
+        json_report['stats']['total'] += len(json_issue['locations'])
+        json_report['stats'][locations[0].rule.severity] += len(json_issue['locations'])
+        json_report['issues'].append(json_issue)
 
     return json_report
 
@@ -1581,6 +1577,15 @@ def make_json_issue(rule_id, locations):
     if not description:
         description = model.message
 
+    # get all unique locations
+    location_keys = set()
+    json_locations = []
+    for location in locations:
+        location_key = (location.row.row_number, location.column.column_number, location.value, location.suggested_value,)
+        if not location_key in location_keys:
+            json_locations.append(make_json_location(location))
+            location_keys.add(location_key)
+
     # make the issue
     json_issue = {
         "rule_id": rule_id,
@@ -1589,7 +1594,7 @@ def make_json_issue(rule_id, locations):
         "severity": model.rule.severity,
         "location_count": len(locations),
         "scope": model.scope,
-        "locations": [make_json_location(location) for location in locations]
+        "locations": json_locations
     }
 
     return json_issue
@@ -1622,4 +1627,10 @@ def make_json_location(location):
 
     return json_location
 
+
+def make_rule_hash(rule):
+    """Make a good-enough hash for a rule."""
+    s = "\r".join([str(rule.severity), str(rule.description), str(rule.tag_pattern)])
+    return base64.urlsafe_b64encode(hashlib.md5(s.encode('utf-8')).digest())[:8].decode('ascii')
+
 # end

Double counting of errors p-code adm name combination consistency errors
When I put the below into data check, I get 2 of every cell eg. F3,F3,F4,F4,F5,F5...
https://data.humdata.org/dataset/77c97850-4004-4285-94db-0b390a962d6e/resource/d6c0dbac-683d-42d7-82b4-a6379bd4f48e/download/mrt_population_statistics_ons_rgph_2013_2017.xlsx
diff --git a/tests/test_validation.py b/tests/test_validation.py
index 43ab00b..7e8b4f3 100644
--- a/tests/test_validation.py
+++ b/tests/test_validation.py
@@ -655,8 +655,8 @@ class TestValidateDataset(unittest.TestCase):
     def test_double_correlation(self):
         """Test correlation when more than one column has same tagspec"""
         SCHEMA = [
-            ['#valid_tag', '#valid_correlation'],
-            ['#adm1+code', '#adm1+name']
+            ['#valid_tag', '#description', '#valid_correlation', '#valid_value+list'],
+            ['#adm1+code', 'xxxxx', '#adm1+name', 'X001|X002']
         ]
         DATASET = [
             ['#adm1+name', '#adm1+code', '#adm1+code'],

pip install -e .
parse_log_pytest
requirements.txt
pytest
3.9
requirements.txt
pytest --no-header -rA --tb=line --color=no -p no:cacheprovider -W ignore::DeprecationWarning
certifi==2025.1.31
charset-normalizer==3.4.1
exceptiongroup==1.2.2
idna==3.10
iniconfig==2.1.0
-e git+https://github.com/HXLStandard/libhxl-python.git@1bc7e92a3844dd443f9e31f478357ea7b599c831#egg=libhxl
packaging==24.2
pluggy==1.5.0
pytest==8.3.5
python-dateutil==2.9.0.post0
python-io-wrapper==0.3.1
requests==2.32.3
six==1.17.0
tomli==2.2.1
Unidecode==1.3.8
urllib3==2.3.0
xlrd==2.0.1

name: libhxl-python
channels:
  - defaults
  - https://repo.anaconda.com/pkgs/main
  - https://repo.anaconda.com/pkgs/r
  - conda-forge
dependencies:
  - _libgcc_mutex=0.1=main
  - _openmp_mutex=5.1=1_gnu
  - ca-certificates=2025.2.25=h06a4308_0
  - ld_impl_linux-64=2.40=h12ee557_0
  - libffi=3.4.4=h6a678d5_1
  - libgcc-ng=11.2.0=h1234567_1
  - libgomp=11.2.0=h1234567_1
  - libstdcxx-ng=11.2.0=h1234567_1
  - ncurses=6.4=h6a678d5_0
  - openssl=3.0.16=h5eee18b_0
  - pip=25.0=py39h06a4308_0
  - python=3.9.21=he870216_1
  - readline=8.2=h5eee18b_0
  - setuptools=75.8.0=py39h06a4308_0
  - sqlite=3.45.3=h5eee18b_0
  - tk=8.6.14=h39e8969_0
  - tzdata=2025a=h04d1e81_0
  - wheel=0.45.1=py39h06a4308_0
  - xz=5.6.4=h5eee18b_1
  - zlib=1.2.13=h5eee18b_1
  - pip:
      - certifi==2025.1.31
      - charset-normalizer==3.4.1
      - exceptiongroup==1.2.2
      - idna==3.10
      - iniconfig==2.1.0
      - packaging==24.2
      - pluggy==1.5.0
      - pytest==8.3.5
      - python-dateutil==2.9.0.post0
      - python-io-wrapper==0.3.1
      - requests==2.32.3
      - six==1.17.0
      - tomli==2.2.1
      - unidecode==1.3.8
      - urllib3==2.3.0
      - xlrd==2.0.1
prefix: /opt/conda/envs/libhxl-python

tests/test_validation.py::TestValidateDataset::test_double_correlation
tests/test_validation.py::TestValidateDataset::test_default_schema tests/test_validation.py::TestLoad::test_load_default tests/test_validation.py::TestJSONSchema::test_truthy tests/test_validation.py::TestJSONReport::test_default tests/test_validation.py::TestJSONReport::test_top_level
tests/test_validation.py::TestTests::test_consistent_datatypes tests/test_validation.py::TestTests::test_correlation tests/test_validation.py::TestTests::test_datatype tests/test_validation.py::TestTests::test_enumeration tests/test_validation.py::TestTests::test_enumeration_suggested_values tests/test_validation.py::TestTests::test_outliers tests/test_validation.py::TestTests::test_range tests/test_validation.py::TestTests::test_regex tests/test_validation.py::TestTests::test_required tests/test_validation.py::TestTests::test_spelling_case_insensitive tests/test_validation.py::TestTests::test_spelling_case_sensitive tests/test_validation.py::TestTests::test_unique_row tests/test_validation.py::TestTests::test_unique_value tests/test_validation.py::TestTests::test_whitespace tests/test_validation.py::TestRule::test_datatype tests/test_validation.py::TestRule::test_range tests/test_validation.py::TestRule::test_regex tests/test_validation.py::TestRule::test_row_restrictions tests/test_validation.py::TestRule::test_value_enumeration tests/test_validation.py::TestRule::test_whitespace tests/test_validation.py::TestValidateColumns::test_bad_value_url tests/test_validation.py::TestValidateColumns::test_min_occurs tests/test_validation.py::TestValidateColumns::test_required tests/test_validation.py::TestValidateRow::test_date tests/test_validation.py::TestValidateRow::test_email tests/test_validation.py::TestValidateRow::test_minmax tests/test_validation.py::TestValidateRow::test_number tests/test_validation.py::TestValidateRow::test_url tests/test_validation.py::TestValidateDataset::test_consistent_datatype tests/test_validation.py::TestValidateDataset::test_correlation tests/test_validation.py::TestValidateDataset::test_different_indicator_datatypes tests/test_validation.py::TestValidateDataset::test_outliers tests/test_validation.py::TestValidateDataset::test_spellings tests/test_validation.py::TestValidateDataset::test_spellings_multiple tests/test_validation.py::TestValidateDataset::test_suggested_value_correlation_key tests/test_validation.py::TestValidateDataset::test_unique_compound tests/test_validation.py::TestValidateDataset::test_unique_single tests/test_validation.py::TestLoad::test_load_bad tests/test_validation.py::TestLoad::test_load_good tests/test_validation.py::TestJSONReport::test_errors
hxl/validation.py
hxl/validation.py