tinyhumansai/openhuman · error

Recovery phrase is required.

Error message

Recovery phrase is required.

What it means

setupLocalWalletFromMnemonic() drives local-wallet creation from a user-entered recovery phrase. After trimming and whitespace-splitting the mnemonic, if zero words remain it throws 'Recovery phrase is required.' — pure input validation before any crypto derivation (deriveAesKeyFromMnemonic, openhumanEncryptSecret) or RPC runs.

Solutions

  1. Disable the submit/continue button until words.length >= 12 (standard BIP-39 length) so the error is unreachable.
  2. Validate in the UI: trim input, count words, show inline 'enter your recovery phrase' feedback before calling the setup function.
  3. If importing programmatically, assert the mnemonic is a non-empty string before invoking.
  4. Check the form component for accidental state resets between screen steps.

Example fix

// before
const canSubmit = true; // always enabled
await setupLocalWalletFromMnemonic({ mnemonic, ... });

// after
const words = mnemonic.trim().split(/\s+/).filter(Boolean);
const canSubmit = words.length === 12 || words.length === 24;
if (!canSubmit) return setFormError('Enter your full recovery phrase.');
await setupLocalWalletFromMnemonic({ mnemonic, ... });
Defensive patterns

Strategy: validation

Validate before calling

const words = mnemonic.trim().split(/\s+/).filter(Boolean);
if (words.length < 12) {
  return setFormError('Enter your full recovery phrase (12 or 24 words).');
}

Type guard

function isNonEmptyMnemonic(v: string): boolean {
  return v.trim().split(/\s+/).filter(Boolean).length >= 12;
}

Try / catch

try {
  await setupLocalWalletFromMnemonic(args);
} catch (err) {
  if (err instanceof Error && err.message === 'Recovery phrase is required.') {
    focusMnemonicField('Enter your recovery phrase to continue.');
    return;
  }
  throw err;
}

Prevention

When it happens

Trigger: Calling the setup flow with an empty string, a whitespace-only string, or a value whose split/filter(Boolean) yields []. Typical UI cause: the submit handler firing before the textarea state binds, or the continue button enabled with an empty field.

Common situations: Form state desync (controlled textarea not wired), paste handler stripping content, autocomplete/password managers clearing the field, or an import flow handing an empty mnemonic through.

Understand the failure class

Background: "missing required argument" and "the following required arguments were not provided": what required-argument errors mean and how to fix them — this error's family across 20 libraries.

Related errors


AI-assisted analysis of tinyhumansai/openhuman@7491200858 (2026-08-17). Data as JSON: /api/errors/3b7a400a4c5233d3. Report an issue: GitHub.

Appendix: source

Thrown at app/src/features/wallet/setupLocalWalletFromMnemonic.ts:22

  deriveWalletAccountsFromMnemonic,
  type WalletSetupSource,
} from '../../utils/cryptoKeys';
import { openhumanEncryptSecret } from '../../utils/tauriCommands/auth';

export async function persistLocalWalletFromMnemonic(args: {
  mnemonic: string;
  source: WalletSetupSource;
  setEncryptionKey: (value: string | null) => Promise<void>;
  /**
   * Set to `true` only when the user has explicitly confirmed wallet replacement
   * through the double-confirmation dialog. Defaults to `false`.
   */
  force?: boolean;
}): Promise<void> {
  const { mnemonic, source, setEncryptionKey, force } = args;
  const words = mnemonic.trim().split(/\s+/).filter(Boolean);
  if (words.length === 0) {
    throw new Error('Recovery phrase is required.');
  }
  const normalizedMnemonic = words.join(' ');
  const aesKey = deriveAesKeyFromMnemonic(normalizedMnemonic);
  const encryptedMnemonic = (await openhumanEncryptSecret(normalizedMnemonic)).result?.trim();
  if (!encryptedMnemonic) {
    throw new Error('Failed to secure recovery phrase. Please try again.');
  }

  await setEncryptionKey(aesKey);
  await setupLocalWallet({
    consentGranted: true,
    source,
    mnemonicWordCount: words.length,
    encryptedMnemonic,
    accounts: deriveWalletAccountsFromMnemonic(normalizedMnemonic),
    force,
  });
}

View on GitHub (pinned to 7491200858)