# Bug 10650 — `libeufin-bank passwd` prompts for a password even when no username is given - **Repository:** `libeufin` (module: libeufin-bank) - **Category:** libeufin-bank / Severity: tweak - **Patch:** [`libeufin.diff`](./libeufin.diff) - **Upstream:** https://bugs.gnunet.org/view.php?id=10650 ## Core issue Running the `passwd` subcommand without the required `username` argument interactively asks for (and confirms) a new password *first*, and only *afterwards* fails with the missing-argument error: ``` $ libeufin-bank passwd Password: Repeat password for confirmation: Usage: libeufin-bank passwd [] [] Error: missing argument ``` The user is forced to type a password twice for a command that was always going to be rejected. The validation of the required `username` argument should happen before any interactive prompting. ## Root cause In `libeufin-bank/src/main/kotlin/tech/libeufin/bank/cli/ChangePw.kt`, the optional `password` argument supplied its default via Clikt's `defaultLazy`, and that lambda performed the interactive `ConfirmationPrompt`: ```kotlin private val username by argument("username", ...) private val password by argument("password", ...) .defaultLazy("prompt") { ... ConfirmationPrompt.create(...).ask()!! // prompts here } ``` Clikt evaluates argument defaults during **argument finalization**, which runs *before* the framework raises `MissingArgument` for the absent required `username`. So the prompt fires as a side effect of finalizing `password`, ahead of the `username` check. Prompting from inside an argument default is the core mistake: a default value's computation should be free of user-visible side effects that depend on other arguments being present. ## Resolution Make `password` a plain optional argument (no side-effecting default) and move the interactive prompt into `run()`, which Clikt only invokes **after** all required arguments have been successfully parsed and validated: ```kotlin private val password by argument("password", ...).optional() private fun promptPassword(): String { /* ConfirmationPrompt … */ } override fun run() = cliCmd(logger) { val rawPassword = password ?: promptPassword() bankConfig(config).withDb { db, cfg -> val password = rawPassword.checkPw(cfg.pwdCheckQuality) ... } } ``` With this change, invoking `passwd` with no `username` exits immediately with the "missing argument " error and never prompts. When `username` *is* provided but `password` is omitted, the prompt behaves exactly as before. The prompt is also now performed before the DB connection is opened, so no connection is held open across the interactive wait. The prompting logic itself (the `ConfirmationPrompt` with hidden input and match confirmation) is preserved verbatim, just relocated into the `promptPassword()` helper. ## Verification - `.optional()` is the standard Clikt argument modifier and is already used the same way in a sibling command in this module (`cli/CreateAccount.kt:76`), so the API and the existing wildcard import `com.github.ajalt.clikt.parameters.arguments.*` cover it. - A full Kotlin compile could **not** be run in this environment: the offline Gradle build aborts before compilation because it cannot fetch the `org.jetbrains.kotlin.jvm` Gradle plugin (no network / plugin not cached). The change is therefore validated by code review against the existing Clikt usage patterns in the same module rather than by compilation.