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
- Run ssh-keygen -t ed25519 manually as the server user to reproduce the underlying failure
- Check permissions: mkdir -p ~/.ssh && chmod 700 ~/.ssh, ensure ownership by the server user
- Install openssh-client if ssh-keygen is absent
- 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
- Pre-install openssh-client in the server image so generation never fails for missing binary
- Ensure ~/.ssh is writable by the server user (700) as part of setup
- Surface data.error verbatim — it contains ssh-keygen stderr needed for diagnosis
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
- data.detail || data.error || `HTTP ${res.status}`
- HTTP ${res.status}
- HTTP ${res.status} ${res.statusText}${msg ? `: ${msg}` : ''}
- HTTP ${res.status}
- data.error || 'scan failed'
AI-assisted analysis of odysseus-dev/odysseus@f9235ebbf1 (2026-08-14).
Data as JSON: /api/errors/c3e8a3b1b1a46e08.
Report an issue: GitHub.