From daad9438baec61ba07db7ba4bae0ae844cc30755 Mon Sep 17 00:00:00 2001 From: Anders Olsson Date: Thu, 4 Jun 2026 14:35:56 +0200 Subject: [PATCH] fix(api): map CHECK-constraint violation (empty key) to 422 --- crates/api/src/admin_objects.rs | 2 ++ crates/api/tests/admin_fields.rs | 21 +++++++++++++++++++++ 2 files changed, 23 insertions(+) diff --git a/crates/api/src/admin_objects.rs b/crates/api/src/admin_objects.rs index 8d3229b..1b2d1b6 100644 --- a/crates/api/src/admin_objects.rs +++ b/crates/api/src/admin_objects.rs @@ -500,6 +500,8 @@ pub(crate) async fn create_field_definition( Some("23505") => Err(StatusCode::CONFLICT), // Referenced vocabulary doesn't exist — client error, not server fault. Some("23503") => Err(StatusCode::UNPROCESSABLE_ENTITY), + // CHECK constraint violated (e.g. empty key) — client error. + Some("23514") => Err(StatusCode::UNPROCESSABLE_ENTITY), _ => Err(StatusCode::INTERNAL_SERVER_ERROR), } } diff --git a/crates/api/tests/admin_fields.rs b/crates/api/tests/admin_fields.rs index a6a9fe0..f4408d3 100644 --- a/crates/api/tests/admin_fields.rs +++ b/crates/api/tests/admin_fields.rs @@ -284,3 +284,24 @@ async fn create_authority_field(pool: PgPool) { assert_eq!(resp.status(), StatusCode::CREATED); } + +#[sqlx::test(migrations = "../db/migrations")] +async fn empty_key_is_422(pool: PgPool) { + migrate_sessions(&db::Db::from_pool(pool.clone())) + .await + .unwrap(); + + seed_user(&pool, "ed@example.com", "pw-editor-123", Role::Editor).await; + + let app = build_app(state(pool)); + let cookie = login(&app, "ed@example.com", "pw-editor-123").await; + + let resp = post_field( + &app, + &cookie, + r#"{"key":"","data_type":"text","required":false,"labels":[{"lang":"en","label":"X"}]}"#, + ) + .await; + + assert_eq!(resp.status(), StatusCode::UNPROCESSABLE_ENTITY); +}