BloopAI/vibe-kanban · error

Server message has invalid length.

Error message

Server message has invalid length.

What it means

finishSpake2Enrollment decodes the server's PAKE2 message from base64 and requires exactly 33 bytes (1 side byte + 32-byte Ed25519 point); any other length throws. It guards against corrupt or protocol-mismatched server responses before point arithmetic.

Source

Thrown at packages/web-core/src/shared/lib/relayPake.ts:78

  return {
    state: {
      passwordBytes,
      passwordScalar,
      xScalar,
      clientMessageBytes: clientPointBytes,
    },
    clientMessageB64: bytesToBase64(clientMessage),
  };
}

export async function finishSpake2Enrollment(
  state: Spake2EnrollmentClientState,
  serverMessageB64: string
): Promise<Uint8Array> {
  const serverMessage = base64ToBytes(serverMessageB64);
  if (serverMessage.length !== 33) {
    throw new Error('Server message has invalid length.');
  }
  if (serverMessage[0] !== 0x42) {
    throw new Error('Server message has invalid side identifier.');
  }

  const serverPointBytes = serverMessage.slice(1);
  const serverPoint = ed25519.ExtendedPoint.fromHex(serverPointBytes);
  const negativePasswordScalar =
    (CURVE_ORDER - state.passwordScalar) % CURVE_ORDER;

  const keyPoint = serverPoint
    .add(SPAKE2_N.multiply(negativePasswordScalar))
    .multiply(state.xScalar);
  const keyPointBytes = keyPoint.toRawBytes();

  return hashAb(
    state.passwordBytes,
    SPAKE2_CLIENT_ID,

View on GitHub (pinned to 4deb7eca8f)

Solutions

  1. Verify you pass the correct response field (the base64 server PAKE message), not the whole response body.
  2. Check client/server protocol versions match (33-byte message = 1 tag byte + 32-byte point).
  3. Log base64ToBytes(serverMessageB64).length on failure to diagnose whether it's truncated or a wrong payload.
  4. Restart the enrollment: if the server errored, its session state may be invalid and startSpake2Enrollment must run again.

Example fix

// before
const sharedKey = await finishSpake2Enrollment(state, serverPayload.data);
// after
if (!serverPayload?.serverMessageB64) {
  throw new Error('No server PAKE message in response');
}
const sharedKey = await finishSpake2Enrollment(state, serverPayload.serverMessageB64);
Defensive patterns

Strategy: validation

Validate before calling

function looksLikeServerPakeMessage(b64: string): boolean {
  try { return atob(b64).length === 33; } catch { return false; }
}
if (!looksLikeServerPakeMessage(serverMessageB64)) {
  throw new Error('Malformed server PAKE message');
}

Type guard

function isServerPakeMessage(msg: Uint8Array): boolean {
  return msg.length === 33 && msg[0] === 0x42;
}

Try / catch

try {
  const key = await finishSpake2Enrollment(state, serverMessageB64);
} catch (e) {
  if (e instanceof Error && e.message.includes('invalid length')) {
    console.error('Server PAKE message malformed; protocol mismatch or error body passed.');
    restartPairing();
  }
}

Prevention

When it happens

Trigger: serverMessageB64 returned by the relay server's /spake2/start response decodes to a length other than 33 — empty string (0 bytes), truncated/corrupted base64, a JSON error body mistakenly passed as the message, or a server speaking a different protocol version.

Common situations: Server returned an error payload instead of the PAKE message and the caller passed the wrong field; base64 with padding/encoding issues; version skew between client (0x42 + 32-byte point) and server message format; man-in-the-middle or proxy mangling the payload.

Related errors


AI-assisted analysis of BloopAI/vibe-kanban@4deb7eca8f (2026-08-29). Data as JSON: /api/errors/a4cda4be80e66c15. Report an issue: GitHub.