{"record":{"id":"942e6e0878fb2616","repo":"ramensoftware/windhawk","slug":"missing-settings-key","errorCode":null,"errorMessage":"Missing settings key","messagePattern":"Missing settings key","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/windhawk-frontend/apps/windhawk-frontend/src/app/panel/mod-details/ModDetails.Website.tsx","lineNumber":106,"sourceCode":"    if (!Array.isArray(settings)) {\n      return null;\n    }\n\n    const parseSettings = (\n      settingsArray: Record<string, unknown>[]\n    ): InitialSettings => {\n      return settingsArray.map(parseSettingItem);\n    };\n\n    const parseSettingItem = (\n      value: Record<string, unknown>\n    ): InitialSettingItem => {\n      // Find the actual setting key (not starting with $)\n      const actualParameters = Object.keys(value).filter(\n        (x) => !x.startsWith('$')\n      );\n      if (actualParameters.length === 0) {\n        throw new Error('Missing settings key');\n      } else if (actualParameters.length > 1) {\n        throw new Error('More than one settings key');\n      }\n\n      const actualParameter = actualParameters[0];\n      const metaParameters = Object.keys(value).filter((x) =>\n        x.startsWith('$')\n      );\n\n      // Group meta parameters by their base name (name, description, options)\n      const metaGroups: Record<\n        string,\n        Array<{ language: string | null; value: unknown }>\n      > = {};\n\n      for (const paramWithPrefix of metaParameters) {\n        const param = paramWithPrefix.slice(1); // remove '$'\n        const paramParts = param.split(':');","sourceCodeStart":88,"sourceCodeEnd":124,"githubUrl":"https://github.com/ramensoftware/windhawk/blob/61d99ed8e182e1af1b60109612b6763ad1b4b74e/src/windhawk-frontend/apps/windhawk-frontend/src/app/panel/mod-details/ModDetails.Website.tsx#L88-L124","documentation":"parseSettingItem processes a mod's setting definition where $-prefixed keys carry metadata and the remaining (non-$) keys must contain exactly one actual setting key. It throws 'Missing settings key' when every key in the object starts with '$', meaning there is no real setting to parse. This guards against malformed mod metadata that would produce a setting item with no name or value.","triggerScenarios":"Rendering mod details (ModDetails.Website.tsx:106) for a mod whose settings definition object contains only metadata keys like { \"$type\": ..., \"$default\": ... } with no actual setting key, or an empty object.","commonSituations":"A mod author published metadata with a typo where the setting key itself was accidentally prefixed with '$' (e.g. '$enabled' instead of 'enabled'); an upstream mods repository format change; truncated/failed JSON fetch yielding a metadata-only fragment.","solutions":["Inspect the mod's settings JSON and ensure exactly one non-$ key exists per setting item (e.g. remove the stray '$' prefix from the setting key).","Refresh/re-fetch the mod details to rule out a stale or truncated cached metadata response.","If a specific mod always triggers this, report the malformed metadata to the mod author or Windhawk maintainers.","Caller-side: wrap parsing in try/catch and skip/reject the offending setting item so one bad entry doesn't break the whole details panel."],"exampleFix":"// before (mod metadata)\n{ \"$type\": \"checkbox\", \"$label\": \"Enable\", \"$enabled\": true }\n// after\n{ \"$type\": \"checkbox\", \"$label\": \"Enable\", \"enabled\": true }","handlingStrategy":"try-catch","validationCode":"const actualKeys = Object.keys(setting).filter((k) => !k.startsWith('$'));\nif (actualKeys.length !== 1) {\n  console.warn('Setting item must have exactly one non-$ key', setting);\n}","typeGuard":null,"tryCatchPattern":"try {\n  items = rawSettings.map(parseSettingItem);\n} catch (e) {\n  if (e.message === 'Missing settings key') {\n    // skip the malformed item and continue rendering\n    items = [];\n  }\n}","preventionTips":["Validate mod metadata against the settings schema before parsing (exactly one non-$ key per item).","Catch per-item parse errors and skip bad entries so one malformed setting doesn't break the panel.","Watch for accidental '$' prefixes on real setting keys when authoring mod metadata."],"tags":["validation","schema","metadata","react"],"backgroundTag":"schema-validation-failed","analyzedSha":"61d99ed8e182e1af1b60109612b6763ad1b4b74e","analyzedAt":"2026-09-12T14:02:41.115Z","contentChangedAt":"2026-09-12T14:02:41.115Z","schemaVersion":2},"datasetVersion":"2026-09-16T09:17:16.951Z"}