{"record":{"id":"13bd7ec608a1266b","repo":"gohugoio/hugo","slug":"npm-pack-malformed-package-json","errorCode":null,"errorMessage":"npm pack: malformed package.json","messagePattern":"npm pack: malformed package\\.json","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"modules/npm/package_builder.go","lineNumber":332,"sourceCode":"\t\t\t\t\t\tinsertion = \",\\n\" + indent + indent + quoted + \"\\n\" + indent\n\t\t\t\t\t}\n\t\t\t\t\tresult := make([]byte, 0, len(data)+len(insertion))\n\t\t\t\t\tresult = append(result, data[:pos]...)\n\t\t\t\t\tresult = append(result, insertion...)\n\t\t\t\t\tresult = append(result, data[pos:]...)\n\t\t\t\t\treturn afero.WriteFile(fsys, packageJSONName, result, 0o666)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// We know a workspaces array exists but could not locate it reliably in the raw data.\n\t\treturn fmt.Errorf(\"npm pack: could not locate existing workspaces array for insertion\")\n\t}\n\n\t// No workspaces key — add before closing brace.\n\tlastBrace := bytes.LastIndexByte(data, '}')\n\tif lastBrace < 0 {\n\t\treturn fmt.Errorf(\"npm pack: malformed package.json\")\n\t}\n\n\tbefore := bytes.TrimRight(data[:lastBrace], \" \\t\\n\\r\")\n\tneedComma := len(before) > 0 && before[len(before)-1] != '{' && before[len(before)-1] != ','\n\n\tvar insertion string\n\tif needComma {\n\t\tinsertion = \",\\n\" + indent + `\"workspaces\": [` + \"\\n\" + indent + indent + quoted + \"\\n\" + indent + \"]\\n\"\n\t} else {\n\t\tinsertion = indent + `\"workspaces\": [` + \"\\n\" + indent + indent + quoted + \"\\n\" + indent + \"]\\n\"\n\t}\n\n\tresult := make([]byte, 0, len(data)+len(insertion))\n\tresult = append(result, before...)\n\tresult = append(result, insertion...)\n\tresult = append(result, data[lastBrace:]...)\n\treturn afero.WriteFile(fsys, packageJSONName, result, 0o666)\n}","sourceCodeStart":314,"sourceCodeEnd":350,"githubUrl":"https://github.com/gohugoio/hugo/blob/52c9bd7908b4d02d4d0ff8f82a888834d6ee10d2/modules/npm/package_builder.go#L314-L350","documentation":"In ensureWorkspaceRef (modules/npm/package_builder.go:332). When there is no existing workspaces key, Hugo inserts one before the last '}' of package.json. bytes.LastIndexByte(data, '}') returns -1 when no closing brace is present, so the file is declared malformed. Although the earlier json.Unmarshal would normally catch bad JSON, this guards the byte-edit step.","triggerScenarios":"package.json has no closing '}' — truncated, or assembled from fragments. This typically implies the file became corrupt after the earlier unmarshal or the read/parse/write path is inconsistent.","commonSituations":"A truncated package.json written by a crashed process; a file stripped of its closing brace by a bad sed/regex edit; concurrent writes during the build.","solutions":["Restore package.json from version control or regenerate it.","Validate it parses as JSON and ends with '}'.","Prevent concurrent writers from touching package.json during 'hugo'."],"exampleFix":"// before: truncated package.json\n{ \"name\": \"proj\", \"dependencies\": { \"x\": \"^1.0\"\n\n// after\n{\n  \"name\": \"proj\",\n  \"dependencies\": { \"x\": \"^1.0\" }\n}","handlingStrategy":"validation","validationCode":"data, err := os.ReadFile(\"package.json\")\nif err != nil {\n    return err\n}\nif bytes.LastIndexByte(data, '}') < 0 {\n    return errors.New(\"package.json is malformed: missing closing brace\")\n}\nvar v any\nreturn json.Unmarshal(data, &v)","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Restore package.json from VCS if truncated.","Prevent concurrent writers during builds.","Validate package.json integrity in CI."],"tags":["hugo","npm-pack","json","corruption"],"backgroundTag":null,"analyzedSha":"52c9bd7908b4d02d4d0ff8f82a888834d6ee10d2","analyzedAt":"2026-08-09T21:49:36.660Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}