thedotmack/claude-mem · error · Error
name must be 1-80 characters
Error message
name must be 1-80 characters
What it means
renameDevice() validates the new device name via normalizeDeviceName(name), which returns null when the trimmed name is empty or longer than 80 characters. The DO throws this error instead of silently accepting a bad rename, so device display names stay within the 1-80 character contract enforced everywhere else in the hub.
Solutions
- Trim the name and check 1 <= name.length <= 80 before calling renameDevice; return a user-facing validation message instead of calling the DO.
- Check the renameDevice return value flow: if the name is fine but the device doesn't exist, rowsWritten is 0 and it returns false (no throw), so distinguish validation failure from not-found.
- Render client-side maxlength=80 on the name input to prevent the error at the source.
- If names can legitimately be longer, store the long label elsewhere; the DO contract is fixed at 80.
Example fix
// before
await hub.renameDevice(deviceId, formData.get('name') as string);
// after
const name = String(formData.get('name') ?? '').trim();
if (name.length < 1 || name.length > 80) {
return json({ error: 'device name must be 1-80 characters' }, 400);
}
await hub.renameDevice(deviceId, name); Defensive patterns
Strategy: validation
Validate before calling
const name = rawName.trim();
if (name.length < 1 || name.length > 80) throw new RangeError('device name must be 1-80 characters'); Type guard
function isValidDeviceName(name: unknown): name is string {
return typeof name === 'string' && name.trim().length >= 1 && name.trim().length <= 80;
} Try / catch
try {
ok = await hub.renameDevice(deviceId, name);
} catch (e) {
if (String(e).includes('name must be 1-80')) return json({ error: 'invalid device name' }, 400);
throw e;
} Prevention
- Enforce maxlength=80 on client name inputs
- Trim whitespace before validating
- Treat renameDevice's false return as 'device not found', distinct from validation errors
When it happens
Trigger: Calling renameDevice(deviceId, name) with an empty/whitespace-only name, a name longer than 80 characters after trimming, or a name that normalizeDeviceName rejects for any other length reason.
Common situations: Clients sending user-supplied device labels without client-side length checks; a UI form that allows blank submits; syncing legacy names from another system that allows 100+ character labels; truncation logic accidentally trimming to empty string.
Understand the failure class
Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.
Related errors
- deviceId must be non-empty
- ops must be an array
- canonical content
- device_id must be 1-128 characters
- ops[ ]
AI-assisted analysis of thedotmack/claude-mem@d8bc9755e7 (2026-09-17).
Data as JSON: /api/errors/6c5d8342fed2c585.
Report an issue: GitHub.
Appendix: source
Thrown at workers/sync-hub/src/do/SyncHub.ts:624
};
});
return {
protocol_version: 1,
user_id: userId,
epoch: this.meta("epoch"),
head_seq: head,
projected_seq: projected,
projection_lag_ops: decimalLag(head, projected),
sync_health: head === projected ? "healthy" : "projector_lagging",
devices,
};
}
renameDevice(deviceId: string, name: string): boolean {
const normalizedId = deviceId.trim();
const normalizedName = normalizeDeviceName(name);
if (normalizedId.length === 0 || normalizedId.length > 128) throw invalid("device_id must be 1-128 characters");
if (normalizedName === null) throw invalid("name must be 1-80 characters");
return this.ctx.storage.sql.exec(
"UPDATE devices SET name = ? WHERE device_id = ?",
normalizedName,
normalizedId,
).rowsWritten > 0;
}
// ---------------------------------------------------------------------
// Authoritative projection checkpoint and short per-user lease.
// ---------------------------------------------------------------------
acquireProjectionLease(targetSeq: string, now = Date.now()): ProjectionLease {
const target = assertCanonicalDecimal(targetSeq);
const head = this.headSeq();
if (compareCanonicalDecimals(target, head) > 0) throw projectionError("target_seq exceeds head_seq");
const projected = this.projectedSeq();
const existingToken = this.metaOptional("projection_lease_token");
const nowValue = this.leaseNow(now);View on GitHub (pinned to d8bc9755e7)