From b26aeefb9a6790124e7a7cb1065695ade5826232 Mon Sep 17 00:00:00 2001 From: anna-parker <50943381+anna-parker@users.noreply.github.com> Date: Tue, 30 Jun 2026 10:15:04 +0200 Subject: [PATCH 1/6] feat(prepro): ensure all internal errors are properly logged and include proper information --- .../processing_functions.py | 198 +++++++----------- 1 file changed, 72 insertions(+), 126 deletions(-) diff --git a/preprocessing/nextclade/src/loculus_preprocessing/processing_functions.py b/preprocessing/nextclade/src/loculus_preprocessing/processing_functions.py index d821f9ecc1..5f33972b5e 100644 --- a/preprocessing/nextclade/src/loculus_preprocessing/processing_functions.py +++ b/preprocessing/nextclade/src/loculus_preprocessing/processing_functions.py @@ -23,7 +23,6 @@ from urllib3.util.retry import Retry from .datatypes import ( - AnnotationSource, AnnotationSourceType, FunctionArgs, InputData, @@ -200,20 +199,23 @@ def format_authors(authors: str) -> str: return "; ".join(loculus_authors).strip() +def raw_internal_error(message: str) -> RawProcessingResult: + """Create a RawProcessingResult for internal/config errors with standard formatting and logging.""" + full_message = f"Internal Error. {message} Please contact the administrator." + logger.error(full_message) + return processing_error(full_message) + + def regex_error( function_name: str, function_arg: str, input_data: InputMetadata, args: FunctionArgs -) -> str: - return ( - f"Internal Error: Function {function_name} did not receive valid " - f"regex {function_arg}, with input {input_data} and args {args}, " - "please contact the administrator." +) -> RawProcessingResult: + return raw_internal_error( + f"{function_name} did not receive a valid {function_arg}, with input {input_data} and args {args}." ) def missing_taxonomy_service_error() -> RawProcessingResult: - return processing_error( - "Configuration error: taxonomy_service_url was None. Please contact the administrator." - ) + return raw_internal_error("taxonomy_service_url was not configured.") def taxonomy_network_error( @@ -221,9 +223,7 @@ def taxonomy_network_error( action: str, e: Exception, ) -> RawProcessingResult: - return processing_error( - f"Internal error: network error while {action} '{subject}': {e}. Please contact the administrator." - ) + return raw_internal_error(f"Network error while {action} '{subject}': {e}.") @dataclass @@ -335,28 +335,17 @@ def call_function( try: result = func(input_data, output_field, input_fields=input_fields, args=args) except Exception as e: - message = ( - f"Error calling function {function_name} for output field {output_field} " - f"with input {input_data} and args {args}: {e}" - ) + message = f"Internal Error. {function_name} raised an unexpected exception for output field '{output_field}': {e}. Please contact the administrator." logger.exception(message) return ProcessingResult( datum=None, warnings=[], errors=[ - ProcessingAnnotation( - processedFields=[ - AnnotationSource(name=output_field, type=AnnotationSourceType.METADATA) - ], - unprocessedFields=[ - AnnotationSource(name=field, type=AnnotationSourceType.METADATA) - for field in input_fields - ], - message=( - f"Internal Error: Function {function_name} did not return " - f"ProcessingResult with input {input_data} and args {args}, " - "please contact the administrator." - ), + ProcessingAnnotation.from_fields( + input_fields, + [output_field], + AnnotationSourceType.METADATA, + message=message, ) ], ) @@ -383,11 +372,8 @@ def call_function( ], ) if not isinstance(result, ProcessingResult): - logger.error( - f"ERROR: Function {function_name} did not return ProcessingResult " - f"given input {input_data} and args {args}. " - "This is likely a preprocessing bug." - ) + message = f"Internal Error. {function_name} returned an unexpected type '{type(result).__name__}'. Please contact the administrator." + logger.error(message) return ProcessingResult( datum=None, warnings=[], @@ -396,11 +382,7 @@ def call_function( input_fields, [output_field], AnnotationSourceType.METADATA, - message=( - f"Internal Error: Function {function_name} did not return " - f"ProcessingResult with input {input_data} and args {args}, " - "please contact the administrator." - ), + message=message, ) ], ) @@ -470,10 +452,8 @@ def parse_date_into_range( # noqa: C901, PLR0912, PLR0915 try: submitted_at = datetime.fromtimestamp(float(str(args["submittedAt"])), tz=pytz.utc) except Exception: - return processing_error( - f"Internal Error: Function parse_into_ranges did not receive valid " - f"submittedAt date, with input {input_data} and args {args}, " - "please contact the administrator." + return raw_internal_error( + f"parse_into_ranges did not receive a valid submittedAt date, with input {input_data} and args {args}." ) max_upper_limit = min(submitted_at, release_date) if release_date else submitted_at @@ -694,10 +674,8 @@ def concatenate( errors: list[str] = [] if not isinstance(args["ACCESSION_VERSION"], str): - return processing_error( - "Internal Error: Function concatenate did not receive " - f"accession_version ProcessingResult with input {input_data} " - f"and args {args}, please contact the administrator." + return raw_internal_error( + f"concatenate did not receive a valid ACCESSION_VERSION (got: {args['ACCESSION_VERSION']!r})." ) accession_version: str = args["ACCESSION_VERSION"] @@ -707,18 +685,10 @@ def concatenate( str(args["fallback_value"]).strip() if args.get("fallback_value") is not None else "" ) - error_msg = ( - "Concatenation failed." - "This may be a configuration error, please contact the administrator." - ) - if not isinstance(order, list): - logger.error( - f"Concatenate: Expected order field to be a list. " - f"This is probably a configuration error. (ACCESSION_VERSION: {accession_version})" + return raw_internal_error( + f"concatenate expected 'order' to be a list (ACCESSION_VERSION: {accession_version})." ) - errors.append(error_msg) - return RawProcessingResult(errors=errors) n_inputs = len(input_data.keys()) # exclude ACCESSION_VERSION as it's provided by _call_preprocessing_function() and should not be an input_metadata field @@ -726,19 +696,13 @@ def concatenate( [i for i in order if i != "ACCESSION_VERSION" and not i.startswith("ARG:")] ) if n_inputs < n_expected: - logger.error( - f"Concatenate: Expected {n_expected} fields, got {n_inputs}. " - f"This is probably a configuration error. (ACCESSION_VERSION: {accession_version})" + return raw_internal_error( + f"concatenate expected {n_expected} input fields but got {n_inputs} (ACCESSION_VERSION: {accession_version})." ) - errors.append(error_msg) - return RawProcessingResult(errors=errors) if not isinstance(field_types, list): - logger.error( - f"Concatenate: Expected type field to be a list. " - f"This is probably a configuration error. (ACCESSION_VERSION: {accession_version})" + return raw_internal_error( + f"concatenate expected 'type' to be a list (ACCESSION_VERSION: {accession_version})." ) - errors.append(error_msg) - return RawProcessingResult(errors=errors) formatted_input_data: list[str] = [] try: @@ -761,7 +725,11 @@ def concatenate( raw_value = str(raw).strip() if raw_value.count("/") > 1: date_string = None - errors.append(error_msg) + errors.extend( + raw_internal_error( + f"dateRangeString field '{order[i]}' has an unexpected format: '{raw_value}' (ACCESSION_VERSION: {accession_version})." + ).errors + ) else: date_string = raw_value.replace("/", " TO ") formatted_input_data.append( @@ -780,12 +748,9 @@ def concatenate( formatted_input_data.append(accession_version) elif field_types[i].startswith("ARG:"): if field_types[i][4:] not in args: - logger.error( - f"Concatenate: Missing argument {field_types[i][4:]} in args. " - f"This is probably a configuration error. (ACCESSION_VERSION: {accession_version})" + return raw_internal_error( + f"concatenate is missing argument '{field_types[i][4:]}' in args (ACCESSION_VERSION: {accession_version})." ) - errors.append(error_msg) - return RawProcessingResult(errors=errors, warnings=warnings) formatted_input_data.append(str(args[field_types[i][4:]])) elif order[i] in input_data: formatted_input_data.append( @@ -794,12 +759,9 @@ def concatenate( else str(input_data[order[i]]).strip() ) else: - logger.error( - f"Concatenate: cannot find field {order[i]} of {field_types[i]} in input_data" - f"This is probably a configuration error. (ACCESSION_VERSION: {accession_version})" + return raw_internal_error( + f"concatenate could not find field '{order[i]}' (type: {field_types[i]}) in input_data (ACCESSION_VERSION: {accession_version})." ) - errors.append(error_msg) - return RawProcessingResult(errors=errors, warnings=warnings) result = "/".join(formatted_input_data) # To avoid downstream issues do not let the result start or end in a "/" @@ -808,12 +770,13 @@ def concatenate( return RawProcessingResult(datum=result, warnings=warnings, errors=errors) except ValueError as e: - logger.error(f"Concatenate failed with {e} (ACCESSION_VERSION: {accession_version})") - errors.append( - f"Concatenation failed for {output_field}. This is a technical error, " - "please contact the administrator." + return RawProcessingResult( + warnings=warnings, + errors=errors + + raw_internal_error( + f"Concatenation failed for '{output_field}' with error: {e} (ACCESSION_VERSION: {accession_version})." + ).errors, ) - return RawProcessingResult(errors=errors, warnings=warnings) @staticmethod def check_authors( @@ -889,9 +852,9 @@ def extract_regex( if not regex_field: return RawProcessingResult() if not isinstance(pattern, str): - return processing_error(regex_error("extract_regex", "pattern", input_data, args)) + return regex_error("extract_regex", "pattern", input_data, args) if not isinstance(capture_group, str): - return processing_error(regex_error("extract_regex", "capture_group", input_data, args)) + return regex_error("extract_regex", "capture_group", input_data, args) match = re.match(pattern, regex_field.strip()) if match: try: @@ -900,10 +863,8 @@ def extract_regex( result = result.upper() return RawProcessingResult(datum=result) except IndexError: - errors.append( - f"The pattern '{pattern}' does not contain a capture group: " - f"'{capture_group}'- this is an internal error," - " please contact your local administrator." + return raw_internal_error( + f"Pattern '{pattern}' does not contain capture group '{capture_group}'." ) else: errors.append( @@ -929,7 +890,7 @@ def check_regex( if not regex_field: return RawProcessingResult() if not isinstance(pattern, str): - return processing_error(regex_error("check_regex", "pattern", input_data, args)) + return regex_error("check_regex", "pattern", input_data, args) regex_field = regex_field.strip() if re.match(pattern, regex_field): return RawProcessingResult(datum=regex_field) @@ -1183,9 +1144,7 @@ def build_display_name( # noqa: C901 submission_id = input_data.get("submissionId", None) warnings: list[str] = [] if submission_id is None: - return processing_error( - "Internal Error: 'submissionId' must not be None for build_display_name(). Please contact the administrator." - ) + return raw_internal_error("'submissionId' must not be None for build_display_name().") order = args.get("order") field_types = args.get("type") @@ -1195,8 +1154,8 @@ def build_display_name( # noqa: C901 or len(order) != len(field_types) or "IDENTIFIER" not in order ): - return processing_error( - "Internal Error: 'order' and 'type' must be lists of equal length, and 'order' must contain IDENTIFIER - this is required for build_display_name to function. Please contact the administrator." + return raw_internal_error( + "'order' and 'type' must be lists of equal length and 'order' must contain IDENTIFIER." ) regex_pattern = args.get("regex_pattern") @@ -1204,8 +1163,8 @@ def build_display_name( # noqa: C901 regex_pattern is not None and "identifier" not in re.compile(str(regex_pattern)).groupindex ): - return processing_error( - "Internal Error: if provided, 'regex_pattern' must contain a named capture group called 'identifier'" + return raw_internal_error( + "If provided, 'regex_pattern' must contain a named capture group called 'identifier'." ) concatenate_order = order.copy() @@ -1326,10 +1285,8 @@ def resolve_host_taxon_id( tax_id = taxon.get("tax_id") if tax_id is None: - return processing_error( - f"Internal error: host validation for '{unvalidated_host}' " - f"was successful but response json 'tax_id' was missing. " - f"Please contact the administrator" + return raw_internal_error( + f"Host validation for '{unvalidated_host}' was successful but response json 'tax_id' was missing." ) return RawProcessingResult(datum=str(tax_id)) @@ -1369,8 +1326,8 @@ def scientific_name_from_id( scientific_name = body.get("scientific_name") if scientific_name is None: - return processing_error( - f"Internal error: '{tax_id}' is a valid taxon ID but response json had no 'scientific_name'. Please contact the administrator" + return raw_internal_error( + f"'{tax_id}' is a valid taxon ID but response json had no 'scientific_name'." ) return RawProcessingResult(datum=scientific_name) @@ -1406,8 +1363,8 @@ def common_name_from_id( common_name = body.get("common_name") if common_name is None: - return processing_error( - f"Internal error: taxonomy service indicated common name was found for hostTaxonId '{tax_id}', but failed to return it. Please contact the administrator." + return raw_internal_error( + f"Taxonomy service indicated common name was found for hostTaxonId '{tax_id}', but failed to return it." ) return RawProcessingResult(datum=common_name) @@ -1432,10 +1389,9 @@ def process_frameshifts(input: str | None) -> InputData: return InputData(datum=format_frameshift(input)) except Exception as e: msg = ( - "Was unable to format frameshift - this is likely an internal error. " - "Please contact the administrator." + f"Internal Error. Frameshift formatting failed: {e}. Please contact the administrator." ) - logger.error(msg + f" Error: {e}") + logger.error(msg) return InputData( datum=None, errors=single_metadata_annotation( @@ -1516,10 +1472,9 @@ def process_stop_codons(input: str | None) -> InputData: return InputData(datum=format_stop_codon(input)) except Exception as e: msg = ( - "Was unable to format stop codon - this is likely an internal error. " - "Please contact the administrator." + f"Internal Error. Stop codon formatting failed: {e}. Please contact the administrator." ) - logger.error(msg + f" Error: {e}") + logger.error(msg) return InputData( datum=None, errors=single_metadata_annotation( @@ -1575,11 +1530,8 @@ def process_mutations_from_clade_founder(input: str | None, args: FunctionArgs | if mutations: return InputData(datum=" ".join(mutations)) except Exception as e: - msg = ( - "Was unable to process mutations from clade founder - this is likely an internal error. " - "Please contact the administrator." - ) - logger.error(msg + f" Error: {e}") + msg = f"Internal Error. Clade founder mutation processing failed: {e}. Please contact the administrator." + logger.error(msg) return InputData( datum=None, errors=single_metadata_annotation( @@ -1609,11 +1561,8 @@ def process_labeled_mutations(input: str | None, args: FunctionArgs | None) -> I if mutations: return InputData(datum=" ".join(mutations)) except Exception as e: - msg = ( - "Was unable to process labeled mutations - this is likely an internal error. " - "Please contact the administrator." - ) - logger.error(msg + f" Error: {e}") + msg = f"Internal Error. Labeled mutation processing failed: {e}. Please contact the administrator." + logger.error(msg) return InputData( datum=None, errors=single_metadata_annotation( @@ -1636,11 +1585,8 @@ def process_phenotype_values(input: str | None, args: FunctionArgs | None) -> In value = entry.get("value") return InputData(datum=str(value) if value is not None else None) except Exception as e: - msg = ( - "Was unable to process phenotype values - this is likely an internal error. " - "Please contact the administrator." - ) - logger.error(msg + f" Error: {e}") + msg = f"Internal Error. Phenotype value processing failed: {e}. Please contact the administrator." + logger.error(msg) return InputData( datum=None, errors=single_metadata_annotation( From f2631a66ab6cb2d6224cb33ad6e9791d85c2497a Mon Sep 17 00:00:00 2001 From: anna-parker <50943381+anna-parker@users.noreply.github.com> Date: Tue, 30 Jun 2026 10:20:18 +0200 Subject: [PATCH 2/6] fix tests --- preprocessing/nextclade/tests/test_host_name_validation.py | 3 ++- preprocessing/nextclade/tests/test_nextclade_preprocessing.py | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/preprocessing/nextclade/tests/test_host_name_validation.py b/preprocessing/nextclade/tests/test_host_name_validation.py index 9f8fd81588..cf6264ac7e 100644 --- a/preprocessing/nextclade/tests/test_host_name_validation.py +++ b/preprocessing/nextclade/tests/test_host_name_validation.py @@ -9,6 +9,7 @@ from loculus_preprocessing.datatypes import ( AnnotationSource, AnnotationSourceType, + FunctionArgs, UnprocessedData, UnprocessedEntry, ) @@ -204,7 +205,7 @@ def test_call_function_converts_raw_errors_to_annotations(mock_session: MagicMoc input_fields = ["host"] output_field = "hostTaxonId" - args = {"taxonomy_service_url": "http://localhost:5000", "is_insdc_ingest_group": False} + args: FunctionArgs = {"taxonomy_service_url": "http://localhost:5000", "is_insdc_ingest_group": False} result = ProcessingFunctions.call_function( "resolve_host_taxon_id", diff --git a/preprocessing/nextclade/tests/test_nextclade_preprocessing.py b/preprocessing/nextclade/tests/test_nextclade_preprocessing.py index 05edf3a4cf..499bd9cfdb 100644 --- a/preprocessing/nextclade/tests/test_nextclade_preprocessing.py +++ b/preprocessing/nextclade/tests/test_nextclade_preprocessing.py @@ -1405,7 +1405,7 @@ def test_process_phenotype_values(): assert process_phenotype_values('[{"name": "NAI","cds": "NA","value": 0.0}]', {}).datum is None invalid = process_phenotype_values("Malformed JSON", {"name": "NAI"}) assert invalid.datum is None - assert "Was unable to process phenotype values" in invalid.errors[0].message + assert "Internal Error. Phenotype value processing failed: invalid syntax" in invalid.errors[0].message def test_reformat_authors_from_loculus_to_embl_style(): From 9d6772fb28e8012ef7e2b3ab2193ffc4ddcd35c2 Mon Sep 17 00:00:00 2001 From: anna-parker <50943381+anna-parker@users.noreply.github.com> Date: Tue, 30 Jun 2026 10:21:23 +0200 Subject: [PATCH 3/6] ruff format --- preprocessing/nextclade/tests/test_host_name_validation.py | 5 ++++- .../nextclade/tests/test_nextclade_preprocessing.py | 5 ++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/preprocessing/nextclade/tests/test_host_name_validation.py b/preprocessing/nextclade/tests/test_host_name_validation.py index cf6264ac7e..d84b53f662 100644 --- a/preprocessing/nextclade/tests/test_host_name_validation.py +++ b/preprocessing/nextclade/tests/test_host_name_validation.py @@ -205,7 +205,10 @@ def test_call_function_converts_raw_errors_to_annotations(mock_session: MagicMoc input_fields = ["host"] output_field = "hostTaxonId" - args: FunctionArgs = {"taxonomy_service_url": "http://localhost:5000", "is_insdc_ingest_group": False} + args: FunctionArgs = { + "taxonomy_service_url": "http://localhost:5000", + "is_insdc_ingest_group": False, + } result = ProcessingFunctions.call_function( "resolve_host_taxon_id", diff --git a/preprocessing/nextclade/tests/test_nextclade_preprocessing.py b/preprocessing/nextclade/tests/test_nextclade_preprocessing.py index 499bd9cfdb..a32d5898d3 100644 --- a/preprocessing/nextclade/tests/test_nextclade_preprocessing.py +++ b/preprocessing/nextclade/tests/test_nextclade_preprocessing.py @@ -1405,7 +1405,10 @@ def test_process_phenotype_values(): assert process_phenotype_values('[{"name": "NAI","cds": "NA","value": 0.0}]', {}).datum is None invalid = process_phenotype_values("Malformed JSON", {"name": "NAI"}) assert invalid.datum is None - assert "Internal Error. Phenotype value processing failed: invalid syntax" in invalid.errors[0].message + assert ( + "Internal Error. Phenotype value processing failed: invalid syntax" + in invalid.errors[0].message + ) def test_reformat_authors_from_loculus_to_embl_style(): From e658cf700a070614f804a167f05302f5e14c00fe Mon Sep 17 00:00:00 2001 From: anna-parker <50943381+anna-parker@users.noreply.github.com> Date: Tue, 30 Jun 2026 10:31:17 +0200 Subject: [PATCH 4/6] claude review of claude --- .../processing_functions.py | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/preprocessing/nextclade/src/loculus_preprocessing/processing_functions.py b/preprocessing/nextclade/src/loculus_preprocessing/processing_functions.py index 5f33972b5e..4ea2740bb1 100644 --- a/preprocessing/nextclade/src/loculus_preprocessing/processing_functions.py +++ b/preprocessing/nextclade/src/loculus_preprocessing/processing_functions.py @@ -199,11 +199,14 @@ def format_authors(authors: str) -> str: return "; ".join(loculus_authors).strip() +def _internal_error_message(message: str) -> str: + full = f"Internal Error. {message} Please contact the administrator." + logger.error(full) + return full + + def raw_internal_error(message: str) -> RawProcessingResult: - """Create a RawProcessingResult for internal/config errors with standard formatting and logging.""" - full_message = f"Internal Error. {message} Please contact the administrator." - logger.error(full_message) - return processing_error(full_message) + return processing_error(_internal_error_message(message)) def regex_error( @@ -725,10 +728,10 @@ def concatenate( raw_value = str(raw).strip() if raw_value.count("/") > 1: date_string = None - errors.extend( - raw_internal_error( + errors.append( + _internal_error_message( f"dateRangeString field '{order[i]}' has an unexpected format: '{raw_value}' (ACCESSION_VERSION: {accession_version})." - ).errors + ) ) else: date_string = raw_value.replace("/", " TO ") @@ -772,10 +775,7 @@ def concatenate( except ValueError as e: return RawProcessingResult( warnings=warnings, - errors=errors - + raw_internal_error( - f"Concatenation failed for '{output_field}' with error: {e} (ACCESSION_VERSION: {accession_version})." - ).errors, + errors=[*errors, _internal_error_message(f"Concatenation failed for '{output_field}' with error: {e} (ACCESSION_VERSION: {accession_version}).")], ) @staticmethod From 5eb9cec8f925dd1c4c7cf29dd859cc5569a36d37 Mon Sep 17 00:00:00 2001 From: anna-parker <50943381+anna-parker@users.noreply.github.com> Date: Tue, 30 Jun 2026 10:32:13 +0200 Subject: [PATCH 5/6] format --- .../src/loculus_preprocessing/processing_functions.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/preprocessing/nextclade/src/loculus_preprocessing/processing_functions.py b/preprocessing/nextclade/src/loculus_preprocessing/processing_functions.py index 4ea2740bb1..79cb95ce8a 100644 --- a/preprocessing/nextclade/src/loculus_preprocessing/processing_functions.py +++ b/preprocessing/nextclade/src/loculus_preprocessing/processing_functions.py @@ -775,7 +775,12 @@ def concatenate( except ValueError as e: return RawProcessingResult( warnings=warnings, - errors=[*errors, _internal_error_message(f"Concatenation failed for '{output_field}' with error: {e} (ACCESSION_VERSION: {accession_version}).")], + errors=[ + *errors, + _internal_error_message( + f"Concatenation failed for '{output_field}' with error: {e} (ACCESSION_VERSION: {accession_version})." + ), + ], ) @staticmethod From 52b66de3a68cf358bb6dec1d0661b1571c0563d1 Mon Sep 17 00:00:00 2001 From: "Anna (Anya) Parker" <50943381+anna-parker@users.noreply.github.com> Date: Thu, 2 Jul 2026 08:42:44 +0200 Subject: [PATCH 6/6] Apply suggestions from code review Co-authored-by: maverbiest <59956362+maverbiest@users.noreply.github.com> --- .../src/loculus_preprocessing/processing_functions.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/preprocessing/nextclade/src/loculus_preprocessing/processing_functions.py b/preprocessing/nextclade/src/loculus_preprocessing/processing_functions.py index 79cb95ce8a..06ca0d1c87 100644 --- a/preprocessing/nextclade/src/loculus_preprocessing/processing_functions.py +++ b/preprocessing/nextclade/src/loculus_preprocessing/processing_functions.py @@ -1566,8 +1566,7 @@ def process_labeled_mutations(input: str | None, args: FunctionArgs | None) -> I if mutations: return InputData(datum=" ".join(mutations)) except Exception as e: - msg = f"Internal Error. Labeled mutation processing failed: {e}. Please contact the administrator." - logger.error(msg) + msg = _internal_error_message(f"Labeled mutation processing failed: {e}. ") return InputData( datum=None, errors=single_metadata_annotation( @@ -1590,8 +1589,7 @@ def process_phenotype_values(input: str | None, args: FunctionArgs | None) -> In value = entry.get("value") return InputData(datum=str(value) if value is not None else None) except Exception as e: - msg = f"Internal Error. Phenotype value processing failed: {e}. Please contact the administrator." - logger.error(msg) + msg = _internal_error_message(f"Labeled mutation processing failed: {e}. ") return InputData( datum=None, errors=single_metadata_annotation(