odysseus-dev/odysseus · error · Error

data.error || 'Failed to generate SSH key'

Error message

data.error || 'Failed to generate SSH key'

What it means

Error thrown when POST /api/cookbook/ssh-key (generate=true) answers with a body where ok is falsy — the endpoint responded, but reported it could not generate/retrieve the local SSH keypair. The message is data.error or a generic fallback.

Source

Thrown at static/js/cookbook-hwfit.js:2401

    const pf = port && port !== '22' ? `-p ${port} ` : '';
    const remote = [
      `KEY=${_singleQuote(publicKey)}`,
      'mkdir -p ~/.ssh',
      'chmod 700 ~/.ssh',
      'touch ~/.ssh/authorized_keys',
      '(grep -qxF "$KEY" ~/.ssh/authorized_keys || printf "%s\\n" "$KEY" >> ~/.ssh/authorized_keys)',
      'chmod 600 ~/.ssh/authorized_keys',
    ].join(' && ');
    return `ssh -o StrictHostKeyChecking=accept-new ${pf}${host} ${_singleQuote(remote)}`;
  }

  async function _fetchCookbookSshKey(generate = false) {
    const res = await fetch('/api/cookbook/ssh-key', {
      method: generate ? 'POST' : 'GET',
      credentials: 'same-origin',
    });
    const data = await res.json();
    if (generate && !data.ok) throw new Error(data.error || 'Failed to generate SSH key');
    return (data.public_key || '').trim();
  }

  async function _populateServerKeyPanel(entry, generate = false) {
    const panel = entry.querySelector('.cookbook-server-key-panel');
    const cmdBox = entry.querySelector('.cookbook-server-key-command');
    const copyBtn = entry.querySelector('.cookbook-server-key-copy');
    const genBtn = entry.querySelector('.cookbook-server-key-gen');
    if (!panel || !cmdBox) return;
    const host = entry.querySelector('.cookbook-srv-host')?.value?.trim() || '';
    const port = entry.querySelector('.cookbook-srv-port')?.value?.trim() || '';
    if (!host || !host.includes('@')) {
      cmdBox.value = 'Enter the server as user@host first.';
      if (copyBtn) copyBtn.disabled = true;
      return;
    }
    if (!/^[A-Za-z0-9._~-]+@[A-Za-z0-9._:-]+$/.test(host) || (port && !/^\d{1,5}$/.test(port))) {
      cmdBox.value = 'Use a plain SSH target like user@host and an optional numeric port.';

View on GitHub (pinned to f9235ebbf1)

Solutions

  1. Run ssh-keygen -t ed25519 manually as the server user to reproduce the underlying failure
  2. Check permissions: mkdir -p ~/.ssh && chmod 700 ~/.ssh, ensure ownership by the server user
  3. Install openssh-client if ssh-keygen is absent
  4. Read data.error in the thrown message — it usually contains the ssh-keygen stderr
Defensive patterns

Strategy: try-catch

Try / catch

try {
  const res = await fetch('/api/cookbook/ssh-key', { method: generate ? 'POST' : 'GET', credentials: 'same-origin' });
  const data = await res.json();
  if (generate && !data.ok) throw new Error(data.error || 'Failed to generate SSH key');
  return (data.public_key || '').trim();
} catch (e) {
  showKeyPanelError(`SSH key ${generate ? 'generation' : 'load'} failed: ${e.message}`);
  return '';
}

Prevention

When it happens

Trigger: POST /api/cookbook/ssh-key returns 200 with {ok:false,error:'ssh-keygen failed...'} because ssh-keygen is missing, the ~/.ssh directory is unwritable, a key exists but is unreadable, or disk/permission issues prevent writing the key file.

Common situations: Server runs as a user without a writable HOME; ssh-keygen not installed in the container/image; ~/.ssh owned by root after a manual edit; read-only filesystem in a container.

Related errors


AI-assisted analysis of odysseus-dev/odysseus@f9235ebbf1 (2026-08-14). Data as JSON: /api/errors/c3e8a3b1b1a46e08. Report an issue: GitHub.