thedotmack/claude-mem · error

sync hub push: acked

Error message

sync hub push: acked[${index}] must be an object

What it means

Inside the push response's acked array, element at the failing index is not an object — it is null, an array, or a JSON primitive. Each ack must be an object with id/kind/origin_local_id/entity_rev/operation_sha256/seq; validation stops before stamping, so no local rows change and flush() retries with backoff.

Solutions

  1. Log/inspect the raw acked array from a curl push to see what element type the hub actually returns
  2. Fix the hub to emit one object per acknowledged op with all six required fields
  3. If nulls represent 'not acked', remove them server-side — the acked array must contain only acknowledgments
  4. Re-deploy hub and client together once the shape matches
Defensive patterns

Strategy: type-guard

Validate before calling

// Validate every ack entry's basic shape (hub tests / custom clients):
function ackEntriesAreObjects(acked: unknown[]): boolean {
  return acked.every(a => !!a && typeof a === 'object' && !Array.isArray(a));
}

Type guard

function isAckedOpShape(v: unknown): v is Record<string, unknown> {
  return !!v && typeof v === 'object' && !Array.isArray(v);
}

Try / catch

if (/acked\[\d+\] must be an object/.test(sync.status().lastError ?? '')) {
  // dump the raw acked array from the hub; nulls/scalars indicate a hub mapping bug
}

Prevention

When it happens

Trigger: Hub emits [null, ...] for skipped ops; a hub bug mapping the wrong list into acked (e.g. seq strings instead of objects); a proxy truncating/reordering JSON; stub fixtures with scalar entries.

Common situations: Hub refactor changed the ack item shape; partial hub deployment serving mixed versions; hand-written mock hub in integration tests.

Understand the failure class

Background: Schema validation failed / invalid input schema: payload rejected because its shape doesn't match the expected schema — this error's family across 28 libraries.

Related errors


AI-assisted analysis of thedotmack/claude-mem@e2d1df569a (2026-08-20). Data as JSON: /api/errors/98ddc9679fc72f0c. Report an issue: GitHub.

Appendix: source

Thrown at src/services/sync/CloudSync.ts:990

    try {
      parsed = await res.json();
    } catch {
      throw new Error('sync hub push: response is not JSON');
    }
    const acked = (parsed as { acked?: unknown } | null)?.acked;
    if (!Array.isArray(acked)) {
      throw new Error('sync hub push: response missing acked array');
    }
    const headSeq = (parsed as { head_seq?: unknown }).head_seq;
    const projectedSeq = (parsed as { projected_seq?: unknown }).projected_seq;
    if (typeof headSeq !== 'string' || typeof projectedSeq !== 'string') {
      throw new Error('sync hub push: response requires decimal-string head_seq/projected_seq');
    }
    assertCanonicalDecimal(headSeq);
    assertCanonicalDecimal(projectedSeq);
    const validatedAcked = acked.map((value, index): AckedOp => {
      if (!value || typeof value !== 'object' || Array.isArray(value)) {
        throw new Error(`sync hub push: acked[${index}] must be an object`);
      }
      const item = value as Record<string, unknown>;
      if (
        typeof item.id !== 'string'
        || typeof item.kind !== 'string'
        || (item.origin_local_id !== null && typeof item.origin_local_id !== 'string')
        || typeof item.entity_rev !== 'string'
        || typeof item.operation_sha256 !== 'string'
        || typeof item.seq !== 'string'
      ) {
        throw new Error(`sync hub push: malformed acked[${index}]`);
      }
      assertCanonicalDecimal(item.entity_rev, { positive: true });
      assertCanonicalDecimal(item.seq, { positive: true });
      if (typeof item.origin_local_id === 'string') assertCanonicalDecimal(item.origin_local_id);
      return item as unknown as AckedOp;
    });
    return {

View on GitHub (pinned to e2d1df569a)