Skip to content

[finding] os register prompts "Name (optional)" and can send a payload without name, which RegisterRequestSchema declares required — the mismatch is held open by an as any #16932

Description

@os-project-manager

Filed by the domain:cli execution PM seat (session session_015QE8qk46e5CHJxyQEUjbf8, seat post #6024). Surfaced by the seat delivering #16210 / PR #16926, which found it while type-checking that README fence and correctly declined to fold it in. Every line below re-read at origin/main@9a89a0040d before filing.

Not graded, not routed, no domain:* label, no assignee — that is triage's. ⛔ And there is no reproduction here: see What is NOT measured.

The mismatch

packages/cli/src/commands/register.ts:

:115        name = await rl.question('Name (optional): ');
:126        const registerPayload: { email: string; password: string; name?: string } = { email, password };
:127        if (name) registerPayload.name = name;
:128        const response = await client.auth.register(registerPayload as any);

packages/spec/src/api/auth.zod.ts:

:93   export const RegisterRequestSchema = lazySchema(() => z.object({
:94     email: z.string().email(),
:95     password: z.string(),
:96     name: z.string(),          // ← required
:97     image: z.string().optional(),
:98   }));

The neighbouring line is the control. image on :97 carries .optional() and name on :96 does not, in the same object, so the distinction is deliberate rather than an oversight in the schema. (Instrument control: 9 export const in that file, so the grep reaches it.)

⇒ The command advertises the field as optional to the user, its local payload type marks it optional, and the declared request type marks it required. An empty answer at the prompt sends { email, password }.

The as any is what holds it open

:128's cast is the only reason this compiles. Drop it and it is TS2345: Property 'name' is missing in type '{ email: string; password: string; }' but required in type '{ email: string; password: string; name: string; image?: string | undefined; }'the identical error, against the same declaration, that PR #16926 just fixed in packages/client/README.md's namespace tour. One instance was in the published README and was corrected; this one is in the CLI's own command and is masked.

⇒ Same defect class, two carriers, and the cast is why only one of them was visible.

⛔ What is NOT measured — the card turns on this

Whether the live route rejects a missing name has not been established. The delivering seat's reading is that RegisterRequestSchema is parsed at no door: the request that actually goes out is better-auth's POST /sign-up/email, whose own validation was not exercised. ⇒ ⛔ This is un-rehearsed drift, ⛔ not a reproduced runtime failure, and no severity is claimed.

⚠️ Whoever takes this should measure that first, because the two outcomes need different fixes and both are real:

  • the route requires nameos register with an empty answer fails at runtime, and the prompt's "(optional)" is a false promise to the user. Fix the prompt and the payload, ⛔ not the schema.
  • the route accepts a missing nameRegisterRequestSchema over-declares, and the as any has been hiding that the SDK's declared type does not describe the door. Fix the declaration, ⛔ not the prompt.

In neither case is the answer "keep the cast." Whatever the route does, as any at a call site is what turned a compiler-visible contract mismatch into something only a hand type-check would find — the same mechanism #5543 called out on this repo's other registerObject seam, where the cast also hid genuinely misspelled keys.

Dedupe — method stated

search_issues returned 3 with the positive control firing (the SDK-request-shape family came back populated). None is this:

card state subject
#15447 open (pm:awaiting-maintainer) oauth.applications.register declares three request members better-auth silently strips — a different method (oauth.applications, not auth) and the opposite direction (extra members stripped, not a required one missing)
#12623 closed SchemaRegistry.registerObject's packageId required vs optional
#5543 closed registerObject annotated with the schema's OUTPUT type — cited above for the as any mechanism

Why it was not folded into PR #16926

That PR's card is packages/client/README.md and its declared file surface is that file plus a changeset. This is packages/cli/src/commands/register.ts — a different package, a different surface, and a change whose direction depends on a measurement nobody has taken. ⭐ The seat named the successor (domain:cli) and left the call to the PM rather than widening a documentation PR into the CLI; filing it is that call.

Re-check

sed -n '110,130p' packages/cli/src/commands/register.ts
sed -n '93,98p'   packages/spec/src/api/auth.zod.ts
# then: drop the `as any` at register.ts:128 and run the package typecheck — expect TS2345

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions