ComposioHQ/composio · error · KeyringError

user must not contain NUL bytes

Error message

user must not contain NUL bytes

What it means

The macOS keyring store validates its inputs before shelling out to the `security` command. Because arguments are passed to a subprocess, embedded NUL bytes (`\0`) would truncate or corrupt the argv string, so the store rejects any `user` value containing NUL bytes with an Invalid KeyringError.

Source

Thrown at ts/packages/cli-keyring/src/stores/macos-security-subprocess.ts:74

  }
  throw new KeyringError({
    kind: 'NotSupportedByStore',
    operation: `macos keychain domain "${domain}"`,
  });
}

function validateSpecifier(service: string, user: string): void {
  // Empty service or user would become wildcards in Keychain Services
  // — keyring-rs throws `Error::Invalid` here and we match that.
  if (service.includes('\0')) {
    throw new KeyringError({
      kind: 'Invalid',
      param: 'service',
      reason: 'service must not contain NUL bytes',
    });
  }
  if (user.includes('\0')) {
    throw new KeyringError({
      kind: 'Invalid',
      param: 'user',
      reason: 'user must not contain NUL bytes',
    });
  }
}

function classifyExitCode(result: SpawnResult, operation: string): KeyringError {
  const code = result.code;
  const stderr = bytesToUtf8(result.stderr).trim();
  if (code === EXIT_ITEM_NOT_FOUND) {
    return new KeyringError({ kind: 'NoEntry' });
  }
  if (code !== null && EXIT_NO_STORAGE_ACCESS.has(code)) {
    return new KeyringError({
      kind: 'NoStorageAccess',
      cause: new Error(stderr || `security ${operation} failed with exit ${code}`),
    });

View on GitHub (pinned to 64b1b85502)

Solutions

  1. Inspect and sanitize the user string before calling the keyring API: strip or reject '\0'.
  2. Trace where the user value originates (env var, config file, IPC payload) and fix the encoding/truncation bug upstream.
  3. If NUL is legitimate in your data, it cannot be a keyring user name — hash or encode it first.

Example fix

// before
await store.setSecret(service, userWithNul, secret); // throws Invalid

// after
if (user.includes('\0')) throw new Error('invalid user');
await store.setSecret(service, user, secret);
Defensive patterns

Strategy: validation

Validate before calling

function isValidKeyringUser(user: string): boolean {
  return user.length > 0 && !user.includes('\0');
}

Try / catch

try {
  await store.setSecret(service, user, secret);
} catch (e) {
  if (e instanceof KeyringError && e.kind === 'Invalid' && e.param === 'user') {
    // sanitize and retry with a cleaned user value
  }
}

Prevention

When it happens

Trigger: Calling setSecret, getSecret, or deleteCredential on the macOS-security store with a user string that contains a '\0' character (e.g. a Buffer read past its data, or malformed serialized credentials passed as the user parameter).

Common situations: Passing binary data or an unsafely-decoded buffer as the user/account name; truncated strings from IPC or JSON with literal \u0000 escapes; fuzzed input reaching the keyring layer.

Related errors


AI-assisted analysis of ComposioHQ/composio@64b1b85502 (2026-08-28). Data as JSON: /api/errors/62b80b8268af7e3c. Report an issue: GitHub.