···5858- The executable differential harness now proves that handle-conflict shape directly for both user and admin handle-update flows, not just local regression tests.
5959- `com.atproto.server.createSession` invalid-credential failures now use the reference runtime’s `401 AuthenticationRequired` shape instead of the older local `AuthRequired` variant.
6060- `com.atproto.admin.sendEmail` now follows the reference runtime’s `400 InvalidRequest` / `Recipient not found` shape for a missing recipient instead of returning a local `404 AccountNotFound`.
6161+- `com.atproto.admin.updateAccountEmail` now follows the reference runtime’s `400 InvalidRequest` / `Account does not exist: ...` shape for a missing account identifier instead of a local `404 AccountNotFound`.
6262+- `com.atproto.admin.deleteAccount` is now reference-style idempotent for missing DIDs: it succeeds and emits the same deleted account event shape instead of failing locally with `404 AccountNotFound`.
6163- `com.atproto.admin.updateAccountPassword` follows the reference runtime’s looser admin policy: it rejects overlong passwords with `400 InvalidRequest` / `Invalid password length.`, but does not impose the normal user-facing minimum-length gate.
6264- `com.atproto.admin.disableAccountInvites` / `enableAccountInvites` now ignore the local `note` field so the visible account state matches the official runtime instead of carrying an extra stored `inviteNote`.
6365- `com.atproto.admin.getInviteCodes` now matches the official runtime on sort validation, always-emitted cursor behavior, total `available` counts, and newest-first `uses` ordering.
+21-11
lib/ATProto/PDS/API/Admin.pm
···180180 require_admin($c);
181181 my $body = $c->req->json || {};
182182 my $account = find_account($c, $body->{account} // q());
183183- xrpc_error(404, 'AccountNotFound', 'Account was not found') unless $account;
183183+ xrpc_error(400, 'InvalidRequest', 'Account does not exist: ' . ($body->{account} // q()))
184184+ unless $account;
184185 update_account_email($c, $account->{did}, $body->{email});
185186 return {};
186187 });
···189190 require_admin($c);
190191 my $body = $c->req->json || {};
191192 my $account = $c->store->get_account_by_did($body->{did} // q());
192192- xrpc_error(404, 'AccountNotFound', 'Account was not found') unless $account;
193193 $c->store->txn(sub ($dbh) {
194194- my $deleted = $c->store->update_account(
195195- $account->{did},
196196- deactivated_at => time,
197197- deleted_at => time,
198198- );
199199- $c->store->revoke_sessions_by_did($account->{did});
200200- $c->store->revoke_app_passwords_by_did($account->{did});
201201- _append_account_event($c, $account->{did}, $deleted, _repo_account_event_payload($deleted, undef));
194194+ my $did = $body->{did} // q();
195195+ my $deleted = $account
196196+ ? $c->store->update_account(
197197+ $did,
198198+ deactivated_at => time,
199199+ deleted_at => time,
200200+ )
201201+ : undef;
202202+ $c->store->revoke_sessions_by_did($did);
203203+ $c->store->revoke_app_passwords_by_did($did);
204204+ my $payload = $deleted
205205+ ? _repo_account_event_payload($deleted, undef)
206206+ : {
207207+ active => JSON::PP::false,
208208+ status => 'deleted',
209209+ };
210210+ _append_account_event($c, $did, $deleted, $payload);
202211 });
203203- return {};
212212+ $c->render(data => q());
213213+ return;
204214 });
205215206216 $registry->register('com.atproto.admin.disableInviteCodes', sub ($c, $endpoint) {