ruvnet/ruflo · error · InMemoryFenceReferenceError

invalid-request

invalid-request

Error message

${label} must be non-empty

What it means

The in-memory fenced-lease reference validates every identity string with a trim-and-non-empty check: repositoryId, sessionId, and workloadId (plus lease scopes via normalizeScope) on the acquire/renew paths. An empty or whitespace-only value throws InMemoryFenceReferenceError with reason 'invalid-request' and a message naming the failed field. This adapter is a single-process conformance reference; distributed adapters enforce the same request shape.

Source

Thrown at v3/@claude-flow/codex/src/harness/in-memory-fenced-lease-reference.ts:34

export class InMemoryFenceReferenceError extends Error {
  constructor(readonly reason: InMemoryFenceRefusal, detail: string) {
    super(detail);
  }
}

interface StoredLease {
  lease: FencedLease;
  expiresAtMs: number;
}

function compare(left: string, right: string): number {
  return left < right ? -1 : left > right ? 1 : 0;
}

function text(value: string, label: string): string {
  const result = value.trim();
  if (!result) throw new InMemoryFenceReferenceError('invalid-request', `${label} must be non-empty`);
  return result;
}

function normalizeScope(kind: LeaseRequest['kind'], value: string): string {
  const scope = text(value, 'lease scope');
  if (scope.startsWith('-') || scope.includes('\\') || scope !== scope.normalize('NFC')) {
    throw new InMemoryFenceReferenceError('invalid-request', `unsafe lease scope: ${value}`);
  }
  if (kind === 'resource') {
    if (scope.includes('/') || scope === '.' || scope === '..') {
      throw new InMemoryFenceReferenceError('invalid-request', `invalid named resource: ${value}`);
    }
    return scope;
  }
  if (
    scope.startsWith('/')
    || scope.split('/').some((part) => !part || part === '.' || part === '..')
  ) {

View on GitHub (pinned to fa13ee4ad6)

Solutions

  1. Check the field named in the message and supply a real value
  2. Derive ids from stable sources (git remote, a session uuid) instead of optional flags
  3. Apply the same trim-and-check guard when building requests so callers get your error message with context

Example fix

// before
ref.acquire({ repositoryId: '', kind: 'resource', scopes: ['db-main'], ttlMs: 30_000, sessionId: 's1', workloadId: 'w1' });

// after
ref.acquire({ repositoryId: 'github.com/org/repo', kind: 'resource', scopes: ['db-main'], ttlMs: 30_000, sessionId: 's1', workloadId: 'w1' });
Defensive patterns

Strategy: try-catch

Validate before calling

function isNonBlank(value: unknown): value is string {
  return typeof value === 'string' && value.trim().length > 0;
}

Type guard

function isNonBlank(value: unknown): value is string {
  return typeof value === 'string' && value.trim().length > 0;
}

Try / catch

try { lease = ref.acquire(request); } catch (error) { if (error instanceof InMemoryFenceReferenceError && error.reason === 'invalid-request') { // fix the field named in error.message, then retry with a corrected request } throw error; }

Prevention

When it happens

Trigger: Calling acquire with repositoryId: '' or a whitespace-only sessionId/workloadId; renew/release paths invoked with blank identity fields; requests assembled from optional variables that were never assigned and default to empty string.

Common situations: Session ids plumbed from optional CLI flags that default to ''; multi-repository loops where one repo's id is missing; user input trimmed to '' before being passed on.

Understand the failure class

Background: "must not be empty", "cannot be empty" — required-field validation errors across open-source libraries — this error's family across 41 libraries.

Related errors


AI-assisted analysis of ruvnet/ruflo@fa13ee4ad6 (2026-08-18). Data as JSON: /api/errors/1cc37fb0eb7ff2c5. Report an issue: GitHub.