nanocoai/nanoclaw · error · Error

bad request shape

Error message

bad request shape

What it means

The ncl Unix-socket server parses each newline-delimited frame as JSON and requires it to match the RequestFrame shape; malformed JSON or a wrong shape yields this error reply (wrapped from JSON.parse or the isRequestFrame guard).

Solutions

  1. Send valid JSON matching the RequestFrame shape the server expects
  2. Use the provided ncl client / socket transport instead of hand-rolled frames
  3. If a version mismatch, restart the socket server and use the matching ncl build

Example fix

# before
echo 'wirings list' | nc -U /path/to/socket
# after (one JSON request frame per line)
echo '{"id":"1","command":"wirings","verb":"list","args":{}}' | nc -U /path/to/socket
Defensive patterns

Strategy: type-guard

Validate before calling

const line = JSON.stringify({ id: '1', command: 'groups', verb: 'list', args: {} });

Type guard

function isRequestFrame(x: unknown): x is RequestFrame {
  return typeof x === 'object' && x !== null && 'id' in x && 'command' in x;
}

Try / catch

try { await handleFrame(conn, line); } catch { write(conn, { id: 'unknown', ok: false, error: { message: 'bad request shape' } }); }

Prevention

When it happens

Trigger: Sending a non-JSON line, or a JSON value missing required RequestFrame fields (id, command structure), to the ncl socket.

Common situations: Custom scripts speaking the socket protocol by hand, version skew between client and server frame format, or debugging with netcat and forgetting quotes.

Related errors


AI-assisted analysis of nanocoai/nanoclaw@294ef2aee8 (2026-08-28). Data as JSON: /api/errors/6550d68fd092546e. Report an issue: GitHub.

Appendix: source

Thrown at src/cli/socket-server.ts:76

    buffer += chunk.toString('utf8');
    let idx: number;
    while ((idx = buffer.indexOf('\n')) >= 0) {
      const line = buffer.slice(0, idx).trim();
      buffer = buffer.slice(idx + 1);
      if (!line) continue;
      void handleFrame(conn, line);
    }
  });
  conn.on('error', (err) => {
    log.warn('ncl CLI server connection error', { err });
  });
}

async function handleFrame(conn: net.Socket, line: string): Promise<void> {
  let req: RequestFrame;
  try {
    const parsed: unknown = JSON.parse(line);
    if (!isRequestFrame(parsed)) throw new Error('bad request shape');
    req = parsed;
  } catch (e) {
    write(conn, {
      id: 'unknown',
      ok: false,
      error: {
        code: 'transport-error',
        message: `bad frame: ${e instanceof Error ? e.message : String(e)}`,
      },
    });
    return;
  }

  // Host caller — connecting to data/ncl.sock requires file-system access
  // to a 0600 socket owned by the host user, so we treat the socket path
  // itself as the auth boundary.
  const ctx: CallerContext = { caller: 'host' };
  const res = await dispatch(req, ctx);

View on GitHub (pinned to 294ef2aee8)