{"record":{"id":"949e946f59b61c67","repo":"Hmbown/CodeWhale","slug":"invalid-local-connection-file","errorCode":null,"errorMessage":"Invalid local connection file.","messagePattern":"Invalid local connection file\\.","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"pet/src/ui/pet.ts","lineNumber":312,"sourceCode":"      if (h.source === 'replay') {\n        mode.querySelector<HTMLOptionElement>('[value=\"replay\"]')!.disabled = false;\n      }\n      message.textContent = 'Restoring the saved habitat…';\n      if (h.checkpoint && h.still === motion.checked) adoptWorld(savedWorld);\n      else await rebuild(h.seconds, undefined, savedWorld.recording(false).start);\n      message.textContent = 'Habitat restored locally. Sound starts only when you enable it.';\n    } else await rebuild();\n    persistenceReady = true; void refreshArchives().catch(() => {});\n  } catch (error) { persistenceFailed = true; get('persistence').textContent = error instanceof Error ? error.message : 'Local persistence is unavailable.'; await rebuild(); }\n  void refreshArchives().catch(() => {});\n  setInterval(() => { void persist(); }, 5000); requestAnimationFrame(animate);\n} catch (error) { message.textContent = error instanceof Error ? error.message : 'Unable to start the habitat.'; }\n\n// Joining is a view switch; existing IndexedDB recordings are retained.\nget<HTMLInputElement>('join-shared').onchange = async event => {\n  const input = event.target as HTMLInputElement, file = input.files?.[0]; if (!file) return;\n  try {\n    if (file.size > 4096) throw new Error('Invalid local connection file.');\n    const d = JSON.parse(await file.text());\n    if (d.version !== 1 || !Number.isSafeInteger(d.port) || d.port < 1 || d.port > 65535 || !/^[a-f0-9]{64}$/i.test(d.token)) throw new Error('Invalid local connection file.');\n    if (!await mayLeave()) return;\n    location.assign(`http://127.0.0.1:${d.port}/#${d.token}`);\n  } catch (e) { message.textContent = e instanceof Error ? e.message : 'Unable to attach.'; }\n  finally { input.value = ''; }\n};\n","sourceCodeStart":294,"sourceCodeEnd":320,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/433685b2024e7bc4c99e1e2e326bcad39b4d9d65/pet/src/ui/pet.ts#L294-L320","documentation":"Thrown when a file chosen via the `join-shared` input exceeds 4096 bytes. A local connection descriptor must be a tiny JSON file, so oversized files are rejected before parsing as a cheap guard against picking the wrong file.","triggerScenarios":"Selecting a large file (e.g. a replay JSON, log, or image) in the join-shared file picker instead of the small connection descriptor.","commonSituations":"Users confusing the replay export file with the connection file; OS dialog defaulting to the wrong directory.","solutions":["Select the actual small connection descriptor file (a few hundred bytes of JSON) generated by the local server.","Re-download or re-generate the connection file from the serving instance.","Check the file size before import: it must be <= 4096 bytes."],"exampleFix":"// before\nconst file = input.files[0]; // 5 MB replay file\n// after\nif (file.size > 4096) { alert('Pick the small connection file, not a replay.'); return; }","handlingStrategy":"validation","validationCode":"if (file.size > 4096) { alert('That is not a connection file (too large).'); return; }","typeGuard":"null","tryCatchPattern":"try { joinShared(file); } catch (e) { showMessage(e.message); } finally { input.value = ''; }","preventionTips":["Name connection files distinctly (e.g. .codewhale-connect.json).","Check file.size before parsing.","Educate users on the difference between replay exports and connection files."],"tags":["file","validation","size-limit"],"backgroundTag":"file-size-limit-exceeded","analyzedSha":"433685b2024e7bc4c99e1e2e326bcad39b4d9d65","analyzedAt":"2026-09-15T12:24:24.634Z","contentChangedAt":"2026-09-15T12:24:24.634Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}