Error handling and validation
Written by Elena David, SEO, ORM, and AI Visibility Consultant.
Version 1.0 separates row-level validation findings from run-stopping failures and converts expected website-import failures into dashboard messages.
Row-level findings
These problems append a ValidationIssue and skip only the affected row:
- one or more empty required values;
- a rating that is not a whole number;
- a whole-number rating outside 1 through 5;
- a date rejected by
date.fromisoformat(); - a duplicate valid
review_idin the same file.
The CLI prints each issue to standard error in this form:
WARNING: current snapshot row 20: rating must be from 1 to 5
After reporting warnings, the program analyzes valid rows, writes reports, and returns status 0 if output succeeds.
Dashboard CSV import messages
The dashboard summarizes the outcome in task-oriented language. A complete current-file import displays Current reviews imported successfully — X reviews ready for analysis. A complete previous-file import displays Previous reviews imported successfully — X reviews available for comparison. The actual count replaces X.
If individual records are rejected, the dashboard reports both imported and rejected counts and identifies the general cause as missing, invalid, or duplicated required information. It does not display record numbers, field names, or parser details in the main interface. The application logger retains each rejected record's source row and detailed reason for diagnosis.
Run-stopping input failures
The CLI catches these exceptions during snapshot loading:
| Failure | Origin | CLI behavior |
|---|---|---|
| File not found | Path.open() raises FileNotFoundError. |
Prints ERROR: ...; returns 1. |
| File permission denied | Path.open() raises PermissionError. |
Prints ERROR: ...; returns 1. |
| Invalid UTF-8 | Loader converts UnicodeDecodeError into ValueError with the path. |
Prints ERROR: File is not valid UTF-8: ...; returns 1. |
| Missing columns | Header validation raises ValueError. |
Prints ERROR: Missing required columns: ...; returns 1. |
If the previous snapshot fails, the current snapshot is not loaded. If either fails, analysis and report generation do not begin.
Report-writing failures
The reporter may raise an OSError for an unwritable or invalid output location. The CLI responds:
ERROR: Could not write reports: details supplied by the operating system
It returns status 1. The CSV is written before the HTML report, so a CSV file can exist even if the later HTML write fails.
Website import failures
The dashboard distinguishes three extraction outcomes:
- Website access blocked: credentials in a URL, local or non-public destinations, Google review surfaces, disallowed or unverifiable robots rules, invalid or excessive redirects, HTTP 401/403/429, or an oversized response.
- No reviews found: the accessible HTML contains no recognizable review records in a supported format.
- Unsupported review HTML: review-like markup exists but does not match JSON-LD, Review Microdata,
article.review, or the configured CSS selectors; invalid selectors and non-HTML responses use this category too.
Timeouts, other network failures, and other HTTP errors are reported as website-import failures. Every redirect is validated before it is requested, and robots rules are checked for a new origin. These failures do not trigger a fallback that bypasses the safeguard.
If a supported structure is detected but its records omit a rating, date, practice name, or review text, the detected format and validation findings remain available for preview. Missing rating and date values are not silently supplied.
User-facing restricted-access response
Technical restriction details are written to application logs, not exposed in the main dashboard. When access restrictions prevent import, the interface displays exactly:
> Reviews couldn't be imported from this website. > > This website restricts automated access. You can still analyze your reviews by uploading a CSV file.
The Switch to CSV Upload button appears directly below the message. Selecting it changes the import method without refreshing the page and preserves the selected maximum alert rating and other relevant session settings. The operator can then upload a current CSV and an optional previous CSV.
This outcome is distinct from an unsupported review format. Restricted access means the importer cannot retrieve permitted page content. Unsupported format means the HTML was accessible but did not match any configured or built-in extractor.
Argument errors
--maximum-rating uses choices=range(1, 6). A value such as 0, 6, or two is rejected by argparse, which prints usage and exits with status 2 before run() begins.
Duplicate behavior
seen_review_ids is maintained separately for each file. When a valid row's identifier already exists in that file, the later row is skipped. Duplicate IDs across previous and current files are expected: they identify known reviews.
The check is exact and case-sensitive. No whitespace remains at the ends of values because all required values are stripped first.
Conditions not handled specially
The application does not provide custom handling for every possible exception or semantic problem. Examples include inconsistent practice details attached to the same review ID, unreasonable future dates, extremely large files, and failures unrelated to the caught exception types.
It also does not validate the HTML after writing or confirm that a downstream application can open the CSV.
Diagnosis sequence
- Identify whether the message starts with
WARNING:orERROR:. - For warnings, inspect the specified CSV row and compare it with the data reference.
- For errors, correct the file, path, permissions, encoding, or output location before rerunning.
- Confirm the process status when automation depends on success or failure.
- Run the pytest suite after changes to application code.
For symptom-based steps, use Troubleshooting.