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); +}