Skip to content

Add specific HTTP errors to all API endpoints#209

Merged
cfs-data merged 27 commits intomainfrom
feature/more_httperrors
Apr 1, 2026
Merged

Add specific HTTP errors to all API endpoints#209
cfs-data merged 27 commits intomainfrom
feature/more_httperrors

Conversation

@laurensWe
Copy link
Copy Markdown
Member

@laurensWe laurensWe commented Mar 25, 2026

Previously, most endpoints would return a generic 500 Internal Server Error when input files were missing or unparseable. This PR maps those failures to proper HTTP status codes:

Error mapping

Exception HTTP Status Scenario
FileNotFoundError 404 Not Found Scan/mark/profile/LR-system file missing
ValueError 422 Unprocessable Entity Empty mask, invalid crop, shape mismatch in compute
json.JSONDecodeError 422 Unprocessable Entity Corrupt mark metadata JSON
KeyError 422 Unprocessable Entity Malformed NPZ file
pydantic.ValidationError 422 Unprocessable Entity Invalid mark metadata fields
other Exception 500 Internal Server Error Unsupported file format, corrupt pickle, anything else

Comment thread src/preprocessors/router.py
Comment thread src/processors/router.py
Copy link
Copy Markdown
Collaborator

@Raytesnel Raytesnel left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

:(

Comment thread src/processors/router.py Outdated
Comment thread src/preprocessors/router.py Outdated
@Raytesnel
Copy link
Copy Markdown
Collaborator

Diff Coverage

Diff: origin/main..HEAD, staged and unstaged changes

* src/preprocessors/router.py (88.9%): Missing lines 116,158,215,279

* src/processors/router.py (84.2%): Missing lines 88-89,155-156,225,255

Summary

* **Total**: 74 lines

* **Missing**: 10 lines

* **Coverage**: 86%

src/preprocessors/router.py

Lines 112-120

  112     vault = create_vault(upload_scan.tag)
  113     try:
  114         parsed_scan = parse_scan_pipeline(upload_scan.scan_file, upload_scan.step_size, upload_scan.step_size)
  115     except FileNotFoundError as e:
! 116         raise HTTPException(status_code=HTTPStatus.NOT_FOUND, detail=f"scan file not found: {e}")
  117     except Exception as e:
  118         raise HTTPException(status_code=HTTPStatus.UNPROCESSABLE_ENTITY, detail=f"could not parse scan file: {e}")
  119     parsed_scan.save_as_x3p(ProcessFiles.scan_image.get_file_path(vault.resource_path))
  120     surface_map_pipeline(

Lines 154-162

  154     vault = create_vault(params.tag)
  155     try:
  156         parsed_image = parse_scan_pipeline(params.scan_file, 1, 1)
  157     except FileNotFoundError as e:
! 158         raise HTTPException(status_code=HTTPStatus.NOT_FOUND, detail=f"scan file not found: {e}")
  159     except Exception as e:
  160         raise HTTPException(status_code=HTTPStatus.UNPROCESSABLE_ENTITY, detail=f"could not parse scan file: {e}")
  161 
  162     try:

Lines 211-219

  211     vault = create_vault(params.tag)
  212     try:
  213         parsed_image = parse_scan_pipeline(params.scan_file, 1, 1)
  214     except FileNotFoundError as e:
! 215         raise HTTPException(status_code=HTTPStatus.NOT_FOUND, detail=f"scan file not found: {e}")
  216     except Exception as e:
  217         raise HTTPException(status_code=HTTPStatus.UNPROCESSABLE_ENTITY, detail=f"could not parse scan file: {e}")
  218 
  219     try:

Lines 275-283

  275     logger.debug(f"Working directory created on: {vault.resource_path}")
  276     try:
  277         parsed_image = parse_scan_pipeline(params.scan_file, 1, 1)
  278     except FileNotFoundError as e:
! 279         raise HTTPException(status_code=HTTPStatus.NOT_FOUND, detail=f"scan file not found: {e}")
  280     except Exception as e:
  281         raise HTTPException(status_code=HTTPStatus.UNPROCESSABLE_ENTITY, detail=f"could not parse scan file: {e}")
  282 
  283     try:

src/processors/router.py

Lines 84-93

  84         mark_comp = load_mark_from_path(path=impression_params.mark_dir_comp, stem="processed")
  85         mark_comp_raw = load_mark_from_path(path=impression_params.mark_dir_comp, stem="mark")
  86     except FileNotFoundError as e:
  87         raise HTTPException(status_code=HTTPStatus.NOT_FOUND, detail=str(e))
! 88     except (ValueError, json.JSONDecodeError, KeyError, ValidationError) as e:
! 89         raise HTTPException(status_code=HTTPStatus.UNPROCESSABLE_ENTITY, detail=str(e))
  90     mark_ref_processed = ProcessedMark(mark_ref, mark_ref_raw)
  91     mark_comp_processed = ProcessedMark(mark_comp, mark_comp_raw)
  92     logger.debug("marks loaded")

Lines 151-160

  151         profile_ref = load_profile_from_path(path=striation_params.mark_dir_ref, stem="profile")
  152         profile_comp = load_profile_from_path(path=striation_params.mark_dir_comp, stem="profile")
  153     except FileNotFoundError as e:
  154         raise HTTPException(status_code=HTTPStatus.NOT_FOUND, detail=str(e))
! 155     except (ValueError, json.JSONDecodeError, KeyError, ValidationError) as e:
! 156         raise HTTPException(status_code=HTTPStatus.UNPROCESSABLE_ENTITY, detail=str(e))
  157     logger.debug("marks & profiles loaded")
  158     comparison_result = compare_striation_marks(
  159         mark_ref=mark_ref, mark_comp=mark_comp, profile_ref=profile_ref, profile_comp=profile_comp
  160     )

Lines 221-229

  221     vault = create_vault(lr_input.tag)
  222     try:
  223         result = process_lr_impression(lr_input=lr_input, working_dir=vault.resource_path)
  224     except FileNotFoundError as e:
! 225         raise HTTPException(status_code=HTTPStatus.NOT_FOUND, detail=str(e))
  226     except Exception as e:
  227         raise HTTPException(status_code=HTTPStatus.UNPROCESSABLE_ENTITY, detail=str(e))
  228     return LRResponse(
  229         urls=LRResponseURL.from_enum(enum=LRFiles, base_url=vault.access_url),

Lines 251-259

  251     vault = create_vault(lr_input.tag)
  252     try:
  253         result = process_lr_striation(lr_input=lr_input, working_dir=vault.resource_path)
  254     except FileNotFoundError as e:
! 255         raise HTTPException(status_code=HTTPStatus.NOT_FOUND, detail=str(e))
  256     except Exception as e:
  257         raise HTTPException(status_code=HTTPStatus.UNPROCESSABLE_ENTITY, detail=str(e))
  258     return LRResponse(
  259         urls=LRResponseURL.from_enum(enum=LRFiles, base_url=vault.access_url),

that are alot of lines that are not tested :). dunno if it belongs to this PR. maybe a ticket if good enoug?

Comment thread src/processors/router.py Outdated
@cfs-data cfs-data requested a review from SimoneAriens April 1, 2026 10:32
Comment thread tests/preprocessors/router/test_process_scan.py Outdated
Comment thread pyproject.toml
@github-actions
Copy link
Copy Markdown

github-actions Bot commented Apr 1, 2026

Diff Coverage

Diff: origin/main..HEAD, staged and unstaged changes

  • src/preprocessors/router.py (100%)
  • src/processors/router.py (100%)

Summary

  • Total: 5 lines
  • Missing: 0 lines
  • Coverage: 100%

@github-actions
Copy link
Copy Markdown

github-actions Bot commented Apr 1, 2026

Code Coverage

Package Line Rate Branch Rate Health
. 96% 92%
computations 94% 67%
container_models 99% 100%
conversion 96% 89%
conversion.export 99% 93%
conversion.filter 97% 89%
conversion.leveling 100% 100%
conversion.leveling.solver 100% 75%
conversion.plots 99% 88%
conversion.preprocess_impression 99% 91%
conversion.preprocess_striation 90% 62%
conversion.profile_correlator 96% 82%
conversion.surface_comparison 99% 89%
conversion.surface_comparison.cell_registration 100% 90%
extractors 97% 75%
mutations 100% 100%
parsers 97% 50%
parsers.patches 89% 60%
preprocessors 100% 100%
processors 100% 83%
renders 99% 50%
utils 71% 100%
Summary 98% (3257 / 3327) 87% (340 / 392)

Minimum allowed line rate is 50%

@cfs-data cfs-data merged commit 34b6cda into main Apr 1, 2026
4 checks passed
@cfs-data cfs-data deleted the feature/more_httperrors branch April 1, 2026 12:12
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants