{"record":{"id":"6c5d8342fed2c585","repo":"thedotmack/claude-mem","slug":"name-must-be-1-80-characters","errorCode":null,"errorMessage":"name must be 1-80 characters","messagePattern":"name must be 1-80 characters","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"workers/sync-hub/src/do/SyncHub.ts","lineNumber":624,"sourceCode":"\t\t\t};\n\t\t});\n\t\treturn {\n\t\t\tprotocol_version: 1,\n\t\t\tuser_id: userId,\n\t\t\tepoch: this.meta(\"epoch\"),\n\t\t\thead_seq: head,\n\t\t\tprojected_seq: projected,\n\t\t\tprojection_lag_ops: decimalLag(head, projected),\n\t\t\tsync_health: head === projected ? \"healthy\" : \"projector_lagging\",\n\t\t\tdevices,\n\t\t};\n\t}\n\n\trenameDevice(deviceId: string, name: string): boolean {\n\t\tconst normalizedId = deviceId.trim();\n\t\tconst normalizedName = normalizeDeviceName(name);\n\t\tif (normalizedId.length === 0 || normalizedId.length > 128) throw invalid(\"device_id must be 1-128 characters\");\n\t\tif (normalizedName === null) throw invalid(\"name must be 1-80 characters\");\n\t\treturn this.ctx.storage.sql.exec(\n\t\t\t\"UPDATE devices SET name = ? WHERE device_id = ?\",\n\t\t\tnormalizedName,\n\t\t\tnormalizedId,\n\t\t).rowsWritten > 0;\n\t}\n\n\t// ---------------------------------------------------------------------\n\t// Authoritative projection checkpoint and short per-user lease.\n\t// ---------------------------------------------------------------------\n\n\tacquireProjectionLease(targetSeq: string, now = Date.now()): ProjectionLease {\n\t\tconst target = assertCanonicalDecimal(targetSeq);\n\t\tconst head = this.headSeq();\n\t\tif (compareCanonicalDecimals(target, head) > 0) throw projectionError(\"target_seq exceeds head_seq\");\n\t\tconst projected = this.projectedSeq();\n\t\tconst existingToken = this.metaOptional(\"projection_lease_token\");\n\t\tconst nowValue = this.leaseNow(now);","sourceCodeStart":606,"sourceCodeEnd":642,"githubUrl":"https://github.com/thedotmack/claude-mem/blob/d8bc9755e74915e5c3b999181e10a67c889bce2a/workers/sync-hub/src/do/SyncHub.ts#L606-L642","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\nawait hub.renameDevice(deviceId, formData.get('name') as string);\n// after\nconst name = String(formData.get('name') ?? '').trim();\nif (name.length < 1 || name.length > 80) {\n  return json({ error: 'device name must be 1-80 characters' }, 400);\n}\nawait hub.renameDevice(deviceId, name);","handlingStrategy":"validation","validationCode":"const name = rawName.trim();\nif (name.length < 1 || name.length > 80) throw new RangeError('device name must be 1-80 characters');","typeGuard":"function isValidDeviceName(name: unknown): name is string {\n  return typeof name === 'string' && name.trim().length >= 1 && name.trim().length <= 80;\n}","tryCatchPattern":"try {\n  ok = await hub.renameDevice(deviceId, name);\n} catch (e) {\n  if (String(e).includes('name must be 1-80')) return json({ error: 'invalid device name' }, 400);\n  throw e;\n}","preventionTips":["Enforce maxlength=80 on client name inputs","Trim whitespace before validating","Treat renameDevice's false return as 'device not found', distinct from validation errors"],"tags":["validation","input-validation","sync"],"backgroundTag":"invalid-argument-value","analyzedSha":"d8bc9755e74915e5c3b999181e10a67c889bce2a","analyzedAt":"2026-09-17T16:40:26.182Z","contentChangedAt":"2026-09-17T16:40:26.182Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}