From 454264db7241175d1b3c2d116b68951de06279c5 Mon Sep 17 00:00:00 2001 From: g1rly-c0d3r Date: Fri, 2 Oct 2026 12:34:52 -0500 Subject: [PATCH 01/10] fix: group schema import exceptions & add logging --- specifyweb/backend/context/views.py | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/specifyweb/backend/context/views.py b/specifyweb/backend/context/views.py index ed50095bf07..8546623a584 100644 --- a/specifyweb/backend/context/views.py +++ b/specifyweb/backend/context/views.py @@ -5,6 +5,7 @@ import json import os import re +import logging from typing import List from xml.etree import ElementTree @@ -14,7 +15,7 @@ from django.db import connection, transaction from django.db.models import Q from django.http import Http404, HttpResponse, HttpResponseBadRequest, \ - HttpResponseForbidden, JsonResponse + HttpResponseForbidden, HttpResponseServerError, JsonResponse from django.urls import URLPattern from django.utils.translation import get_language_info from django.utils.translation import gettext as _ @@ -46,6 +47,8 @@ is_collection_available, is_guided_setup_complete, ) + +logger = logging.getLogger(__name__) def set_collection_cookie(response, collection_id): # pragma: no cover response.set_cookie('collection', str(collection_id), max_age=365*24*60*60) @@ -655,13 +658,13 @@ def schema_localization_import(request): if not isinstance(language, str) or not re.fullmatch( r'[A-Za-z]{2}(?:-[A-Za-z]{2})?', language ): - raise ValueError + raise ValueError(f"Invalid language string: {language}") if isinstance(schema, dict) and {'language', 'schema'} <= schema.keys(): source_language = schema['language'] if not isinstance(source_language, str) or ( source_language.lower() != language.lower() ): - raise ValueError + raise ValueError(f"Invalid Localization (expected {source_language}, got {language}") schema = schema['schema'] references = { 'format': _schema_import_resource_names( @@ -704,8 +707,12 @@ def schema_localization_import(request): request.specify_collection, request.specify_user_agent, model.__name__, data ) - except (AttributeError, KeyError, TypeError, ValueError, json.JSONDecodeError): - return HttpResponseBadRequest() + except (ValueError, json.JSONDecodeError) as err: + return HttpResponseBadRequest(f"{{\"original error\": \"{err}\" }}", content_type="text/json") + except (AttributeError, KeyError, TypeError) as err: + logger.warning(f"Schema Import failed: {err}") + return HttpResponseServerError(err, content_type="text/plain") + return JsonResponse({'updated': len(operations)}) view_parameters_schema = [ From a23a3189d173238e8f7f06a340a82d95b5c4d6e7 Mon Sep 17 00:00:00 2001 From: "Caroline D." <108160931+CarolineDenis@users.noreply.github.com> Date: Tue, 6 Oct 2026 14:12:16 +0200 Subject: [PATCH 02/10] Potential fix for pull request finding 'CodeQL / Information exposure through an exception' Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> --- specifyweb/backend/context/views.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/specifyweb/backend/context/views.py b/specifyweb/backend/context/views.py index 8546623a584..9c6cd620130 100644 --- a/specifyweb/backend/context/views.py +++ b/specifyweb/backend/context/views.py @@ -708,7 +708,11 @@ def schema_localization_import(request): model.__name__, data ) except (ValueError, json.JSONDecodeError) as err: - return HttpResponseBadRequest(f"{{\"original error\": \"{err}\" }}", content_type="text/json") + logger.warning("Schema import validation failed", exc_info=True) + return JsonResponse( + {"error": "Invalid schema localization import payload."}, + status=400 + ) except (AttributeError, KeyError, TypeError) as err: logger.warning(f"Schema Import failed: {err}") return HttpResponseServerError(err, content_type="text/plain") From 4ce18f89d4659e9f1cf5e8a56de332ae576006ae Mon Sep 17 00:00:00 2001 From: "Caroline D." <108160931+CarolineDenis@users.noreply.github.com> Date: Tue, 6 Oct 2026 14:12:27 +0200 Subject: [PATCH 03/10] Potential fix for pull request finding 'CodeQL / Information exposure through an exception' Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> --- specifyweb/backend/context/views.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/specifyweb/backend/context/views.py b/specifyweb/backend/context/views.py index 9c6cd620130..e3185449c37 100644 --- a/specifyweb/backend/context/views.py +++ b/specifyweb/backend/context/views.py @@ -715,7 +715,7 @@ def schema_localization_import(request): ) except (AttributeError, KeyError, TypeError) as err: logger.warning(f"Schema Import failed: {err}") - return HttpResponseServerError(err, content_type="text/plain") + return HttpResponseServerError("An internal error has occurred.", content_type="text/plain") return JsonResponse({'updated': len(operations)}) From d0dadbce042bbec9a11805fd7a37bf38907d48d9 Mon Sep 17 00:00:00 2001 From: g1rly-c0d3r Date: Tue, 6 Oct 2026 12:32:25 -0500 Subject: [PATCH 04/10] fix: add reporting on the front-end to distinguish error types --- .../lib/components/SchemaConfig/Layout.tsx | 38 ++++++------------- .../js_src/lib/localization/schema.ts | 26 ++++++++++++- 2 files changed, 37 insertions(+), 27 deletions(-) diff --git a/specifyweb/frontend/js_src/lib/components/SchemaConfig/Layout.tsx b/specifyweb/frontend/js_src/lib/components/SchemaConfig/Layout.tsx index 161dfc818d4..8f41091e6c3 100644 --- a/specifyweb/frontend/js_src/lib/components/SchemaConfig/Layout.tsx +++ b/specifyweb/frontend/js_src/lib/components/SchemaConfig/Layout.tsx @@ -24,6 +24,7 @@ import { useSchemaConfig, } from './Store'; import { dialogIcons } from '../Atoms/Icons'; +import { raise } from '../Errors/Crash'; export function SchemaConfigLayout(): JSX.Element { const schemaData = useOutletContext(); @@ -55,8 +56,7 @@ function SchemaConfigLayoutContent(): JSX.Element { const setSingleResource = React.useContext(SetSingleResourceContext); const loading = React.useContext(LoadingContext); const [importFile, setImportFile] = React.useState(); - const [importError, setImportError] = React.useState(false); - const [importSuccessful, setImportSuccessful] = React.useState(false); + const [importStatus, setImportStatus] = React.useState(0); const [importRefreshError, setImportRefreshError] = React.useState(false); React.useEffect(() => { @@ -80,7 +80,6 @@ function SchemaConfigLayoutContent(): JSX.Element { ); }; const handleImport = (file: File): void => { - setImportError(false); setImportFile(file); }; const confirmImport = (): void => { @@ -95,17 +94,22 @@ function SchemaConfigLayoutContent(): JSX.Element { method: 'POST', headers: { Accept: 'application/json' }, body: { schema, language: rawLanguage }, - errorMode: 'silent', + expectedErrors: [400, 504] }) ) .then( - () => setImportSuccessful(true), - () => setImportError(true) + (response) => { + setImportStatus(response.status); + if (response.status != 200) + raise({name: schemaText.importSchemaError({schemaConfig: schemaText.schemaConfig()}), message: + response.status === 504 ? schemaText.importSchemaErrorTimeout({schemaConfig: schemaText.schemaConfig()}) : schemaText.importSchemaErrorBadRequest({schemaConfig: schemaText.schemaConfig()}) + }) + } ) ); }; const closeImportSuccess = (): void => { - setImportSuccessful(false); + setImportStatus(0); loading( handleSchemaSaved(rawLanguage, tableName).catch(() => setImportRefreshError(true) @@ -170,25 +174,7 @@ function SchemaConfigLayoutContent(): JSX.Element {

)} - {importError && ( - {commonText.close()} - } - icon={dialogIcons.error} - header={schemaText.importSchema({ - schemaConfig: schemaText.schemaConfig(), - })} - onClose={(): void => setImportError(false)} - > -

- {schemaText.importSchemaError({ - schemaConfig: schemaText.schemaConfig(), - })} -

-
- )} - {importSuccessful && ( + {importStatus == 200 && ( {commonText.close()} diff --git a/specifyweb/frontend/js_src/lib/localization/schema.ts b/specifyweb/frontend/js_src/lib/localization/schema.ts index bb0d69c5555..f88074a3dbe 100644 --- a/specifyweb/frontend/js_src/lib/localization/schema.ts +++ b/specifyweb/frontend/js_src/lib/localization/schema.ts @@ -92,7 +92,31 @@ export const schemaText = createDictionary({ }, importSchemaError: { 'en-us': - 'The {schemaConfig:string} export is invalid or was exported for a different language and cannot be imported.', + '{schemaConfig:string} import failed:', + 'de-ch': '', + 'es-es': '', + 'fr-fr': '', + 'hr-hr': '', + nb: '', + 'pt-br': '', + 'ru-ru': '', + 'uk-ua': '', + }, + importSchemaErrorBadRequest: { + 'en-us': + "The {schemaConfig:string} export is invalid or was exported for a different language and cannot be imported.", + 'de-ch': '', + 'es-es': '', + 'fr-fr': '', + 'hr-hr': '', + nb: '', + 'pt-br': '', + 'ru-ru': '', + 'uk-ua': '', + }, + importSchemaErrorTimeout: { + 'en-us': + 'Network request timed out, Specify is still importing the {schemaConfig:string} export in the background.', 'de-ch': '', 'es-es': '', 'fr-fr': '', From 34340426692fc304eb334198a10de0039d4c5403 Mon Sep 17 00:00:00 2001 From: g1rly-c0d3r Date: Tue, 6 Oct 2026 17:35:37 +0000 Subject: [PATCH 05/10] Lint code with ESLint and Prettier Triggered by d0dadbce042bbec9a11805fd7a37bf38907d48d9 on branch refs/heads/issue-8617 --- specifyweb/frontend/js_src/lib/localization/schema.ts | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/specifyweb/frontend/js_src/lib/localization/schema.ts b/specifyweb/frontend/js_src/lib/localization/schema.ts index f88074a3dbe..1a0251f0b0f 100644 --- a/specifyweb/frontend/js_src/lib/localization/schema.ts +++ b/specifyweb/frontend/js_src/lib/localization/schema.ts @@ -91,8 +91,7 @@ export const schemaText = createDictionary({ 'uk-ua': '', }, importSchemaError: { - 'en-us': - '{schemaConfig:string} import failed:', + 'en-us': '{schemaConfig:string} import failed:', 'de-ch': '', 'es-es': '', 'fr-fr': '', @@ -104,7 +103,7 @@ export const schemaText = createDictionary({ }, importSchemaErrorBadRequest: { 'en-us': - "The {schemaConfig:string} export is invalid or was exported for a different language and cannot be imported.", + 'The {schemaConfig:string} export is invalid or was exported for a different language and cannot be imported.', 'de-ch': '', 'es-es': '', 'fr-fr': '', From 9248910d11d00db099ab5eabd40dbcdb149f23be Mon Sep 17 00:00:00 2001 From: g1rly-c0d3r Date: Tue, 6 Oct 2026 12:53:27 -0500 Subject: [PATCH 06/10] chore: update timeout message to indicate that specify might not finish importing --- specifyweb/frontend/js_src/lib/localization/schema.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/specifyweb/frontend/js_src/lib/localization/schema.ts b/specifyweb/frontend/js_src/lib/localization/schema.ts index 1a0251f0b0f..0d4c62d8f53 100644 --- a/specifyweb/frontend/js_src/lib/localization/schema.ts +++ b/specifyweb/frontend/js_src/lib/localization/schema.ts @@ -115,7 +115,7 @@ export const schemaText = createDictionary({ }, importSchemaErrorTimeout: { 'en-us': - 'Network request timed out, Specify is still importing the {schemaConfig:string} export in the background.', + 'Network request timed out, Specify may or may not still import the {schemaConfig:string} export in the background. Please check back in later to see if your changes have been applied.', 'de-ch': '', 'es-es': '', 'fr-fr': '', From 1cc291a3161c8c6904489eb406f12c4082159bcf Mon Sep 17 00:00:00 2001 From: g1rly-c0d3r Date: Tue, 6 Oct 2026 12:58:34 -0500 Subject: [PATCH 07/10] fix: move `AttributeError` to the Bad Request path --- specifyweb/backend/context/views.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/specifyweb/backend/context/views.py b/specifyweb/backend/context/views.py index e3185449c37..53e3a98c03f 100644 --- a/specifyweb/backend/context/views.py +++ b/specifyweb/backend/context/views.py @@ -707,13 +707,13 @@ def schema_localization_import(request): request.specify_collection, request.specify_user_agent, model.__name__, data ) - except (ValueError, json.JSONDecodeError) as err: + except (AttributeError, ValueError, json.JSONDecodeError) as err: logger.warning("Schema import validation failed", exc_info=True) return JsonResponse( {"error": "Invalid schema localization import payload."}, status=400 ) - except (AttributeError, KeyError, TypeError) as err: + except (KeyError, TypeError) as err: logger.warning(f"Schema Import failed: {err}") return HttpResponseServerError("An internal error has occurred.", content_type="text/plain") From 73255424504153cb3b31591164a94f6f628ccad7 Mon Sep 17 00:00:00 2001 From: g1rly-c0d3r Date: Tue, 6 Oct 2026 14:16:33 -0500 Subject: [PATCH 08/10] fix: use strict equality for checking schema import response and handle errors correctly --- .../frontend/js_src/lib/components/SchemaConfig/Layout.tsx | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/specifyweb/frontend/js_src/lib/components/SchemaConfig/Layout.tsx b/specifyweb/frontend/js_src/lib/components/SchemaConfig/Layout.tsx index 8f41091e6c3..82fcf722031 100644 --- a/specifyweb/frontend/js_src/lib/components/SchemaConfig/Layout.tsx +++ b/specifyweb/frontend/js_src/lib/components/SchemaConfig/Layout.tsx @@ -100,11 +100,12 @@ function SchemaConfigLayoutContent(): JSX.Element { .then( (response) => { setImportStatus(response.status); - if (response.status != 200) + if (response.status !== 200) raise({name: schemaText.importSchemaError({schemaConfig: schemaText.schemaConfig()}), message: response.status === 504 ? schemaText.importSchemaErrorTimeout({schemaConfig: schemaText.schemaConfig()}) : schemaText.importSchemaErrorBadRequest({schemaConfig: schemaText.schemaConfig()}) }) - } + }, + (err) => raise(err) ) ); }; From 3e2e4c6a6ff869aa8217c97f5b86b117b530d432 Mon Sep 17 00:00:00 2001 From: g1rly-c0d3r Date: Tue, 6 Oct 2026 16:11:09 -0500 Subject: [PATCH 09/10] fix: also include 500 error in schema import reporting --- .../js_src/lib/components/SchemaConfig/Layout.tsx | 6 ++++-- .../frontend/js_src/lib/localization/schema.ts | 12 ++++++++++++ 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/specifyweb/frontend/js_src/lib/components/SchemaConfig/Layout.tsx b/specifyweb/frontend/js_src/lib/components/SchemaConfig/Layout.tsx index 82fcf722031..ac53a147a8d 100644 --- a/specifyweb/frontend/js_src/lib/components/SchemaConfig/Layout.tsx +++ b/specifyweb/frontend/js_src/lib/components/SchemaConfig/Layout.tsx @@ -94,7 +94,7 @@ function SchemaConfigLayoutContent(): JSX.Element { method: 'POST', headers: { Accept: 'application/json' }, body: { schema, language: rawLanguage }, - expectedErrors: [400, 504] + expectedErrors: [400, 500, 504] }) ) .then( @@ -102,7 +102,9 @@ function SchemaConfigLayoutContent(): JSX.Element { setImportStatus(response.status); if (response.status !== 200) raise({name: schemaText.importSchemaError({schemaConfig: schemaText.schemaConfig()}), message: - response.status === 504 ? schemaText.importSchemaErrorTimeout({schemaConfig: schemaText.schemaConfig()}) : schemaText.importSchemaErrorBadRequest({schemaConfig: schemaText.schemaConfig()}) + response.status === 504 ? schemaText.importSchemaErrorTimeout({schemaConfig: schemaText.schemaConfig()}) : + response.status === 400 ? schemaText.importSchemaErrorBadRequest({schemaConfig: schemaText.schemaConfig()}) : + schemaText.importSchemaErrorInternalError() }) }, (err) => raise(err) diff --git a/specifyweb/frontend/js_src/lib/localization/schema.ts b/specifyweb/frontend/js_src/lib/localization/schema.ts index 0d4c62d8f53..25450db8ca7 100644 --- a/specifyweb/frontend/js_src/lib/localization/schema.ts +++ b/specifyweb/frontend/js_src/lib/localization/schema.ts @@ -125,6 +125,18 @@ export const schemaText = createDictionary({ 'ru-ru': '', 'uk-ua': '', }, + importSchemaErrorInternalError: { + 'en-us': + 'Specify experienced an error, please clear your cache and try again.', + 'de-ch': '', + 'es-es': '', + 'fr-fr': '', + 'hr-hr': '', + nb: '', + 'pt-br': '', + 'ru-ru': '', + 'uk-ua': '', + }, importSchemaSuccess: { 'en-us': '{schemaConfig:string} imported successfully', 'de-ch': '', From 6b6c4ab401644a5452d01fa1ad3b8957f93fd315 Mon Sep 17 00:00:00 2001 From: "Caroline D." <108160931+CarolineDenis@users.noreply.github.com> Date: Wed, 7 Oct 2026 09:59:16 +0200 Subject: [PATCH 10/10] Clean up localization schema by removing empty entries Removed empty localization entries for multiple languages in the import schema. --- .../js_src/lib/localization/schema.ts | 120 ------------------ 1 file changed, 120 deletions(-) diff --git a/specifyweb/frontend/js_src/lib/localization/schema.ts b/specifyweb/frontend/js_src/lib/localization/schema.ts index 25450db8ca7..208973c7397 100644 --- a/specifyweb/frontend/js_src/lib/localization/schema.ts +++ b/specifyweb/frontend/js_src/lib/localization/schema.ts @@ -11,176 +11,56 @@ import { createDictionary } from './utils'; export const schemaText = createDictionary({ importSchema: { 'en-us': 'Import {schemaConfig:string}', - 'de-ch': '', - 'es-es': '', - 'fr-fr': '', - 'hr-hr': '', - nb: '', - 'pt-br': '', - 'ru-ru': '', - 'uk-ua': '', }, importSchemaWarning: { 'en-us': 'Importing a {schemaConfig:string} will overwrite the current one. This action cannot be undone.', - 'de-ch': '', - 'es-es': '', - 'fr-fr': '', - 'hr-hr': '', - nb: '', - 'pt-br': '', - 'ru-ru': '', - 'uk-ua': '', }, importSchemaLanguageWarning: { 'en-us': 'The exported schema language must match the language currently being configured.', - 'de-ch': '', - 'es-es': '', - 'fr-fr': '', - 'hr-hr': '', - nb: '', - 'pt-br': '', - 'ru-ru': '', - 'uk-ua': '', }, importSchemaBackupPrompt: { 'en-us': 'We strongly recommend downloading a backup of the current {schemaConfig:string} before importing.', - 'de-ch': '', - 'es-es': '', - 'fr-fr': '', - 'hr-hr': '', - nb: '', - 'pt-br': '', - 'ru-ru': '', - 'uk-ua': '', }, importSchemaLimitations: { 'en-us': 'This import will not assign pick lists, field formats, or web links unless they already exist.', - 'de-ch': '', - 'es-es': '', - 'fr-fr': '', - 'hr-hr': '', - nb: '', - 'pt-br': '', - 'ru-ru': '', - 'uk-ua': '', }, downloadSchemaBackup: { 'en-us': 'Export {schemaConfig:string}', - 'de-ch': '', - 'es-es': '', - 'fr-fr': '', - 'hr-hr': '', - nb: '', - 'pt-br': '', - 'ru-ru': '', - 'uk-ua': '', }, importSchemaContinue: { 'en-us': 'Continue Import', - 'de-ch': '', - 'es-es': '', - 'fr-fr': '', - 'hr-hr': '', - nb: '', - 'pt-br': '', - 'ru-ru': '', - 'uk-ua': '', }, importSchemaError: { 'en-us': '{schemaConfig:string} import failed:', - 'de-ch': '', - 'es-es': '', - 'fr-fr': '', - 'hr-hr': '', - nb: '', - 'pt-br': '', - 'ru-ru': '', - 'uk-ua': '', }, importSchemaErrorBadRequest: { 'en-us': 'The {schemaConfig:string} export is invalid or was exported for a different language and cannot be imported.', - 'de-ch': '', - 'es-es': '', - 'fr-fr': '', - 'hr-hr': '', - nb: '', - 'pt-br': '', - 'ru-ru': '', - 'uk-ua': '', }, importSchemaErrorTimeout: { 'en-us': 'Network request timed out, Specify may or may not still import the {schemaConfig:string} export in the background. Please check back in later to see if your changes have been applied.', - 'de-ch': '', - 'es-es': '', - 'fr-fr': '', - 'hr-hr': '', - nb: '', - 'pt-br': '', - 'ru-ru': '', - 'uk-ua': '', }, importSchemaErrorInternalError: { 'en-us': 'Specify experienced an error, please clear your cache and try again.', - 'de-ch': '', - 'es-es': '', - 'fr-fr': '', - 'hr-hr': '', - nb: '', - 'pt-br': '', - 'ru-ru': '', - 'uk-ua': '', }, importSchemaSuccess: { 'en-us': '{schemaConfig:string} imported successfully', - 'de-ch': '', - 'es-es': '', - 'fr-fr': '', - 'hr-hr': '', - nb: '', - 'pt-br': '', - 'ru-ru': '', - 'uk-ua': '', }, importSchemaSuccessDescription: { 'en-us': 'The imported {schemaConfig:string} has been saved.', - 'de-ch': '', - 'es-es': '', - 'fr-fr': '', - 'hr-hr': '', - nb: '', - 'pt-br': '', - 'ru-ru': '', - 'uk-ua': '', }, importSchemaRefreshError: { 'en-us': 'The import succeeded, but the page could not refresh automatically. Reload the page to see the imported changes.', - 'de-ch': '', - 'es-es': '', - 'fr-fr': '', - 'hr-hr': '', - nb: '', - 'pt-br': '', - 'ru-ru': '', - 'uk-ua': '', }, reloadSchemaConfig: { 'en-us': 'Reload Page', - 'de-ch': '', - 'es-es': '', - 'fr-fr': '', - 'hr-hr': '', - nb: '', - 'pt-br': '', - 'ru-ru': '', - 'uk-ua': '', }, table: { 'en-us': 'Table',