{"record":{"id":"5fe2125d23f00922","repo":"composer/composer","slug":"the-json-file-must-be-an-object","errorCode":null,"errorMessage":"The json file must be an object ({})","messagePattern":"The json file must be an object \\((.+?)\\)","errorType":"validation","errorClass":"InvalidArgumentException","httpStatus":null,"severity":"error","filePath":"src/Composer/Json/JsonManipulator.php","lineNumber":48,"sourceCode":"       (?<object>    \\{  (?:  (?&pair)  (?: , (?&pair)  )*+  )?+  \\s*+ \\} )\n       (?<json>      \\s*+ (?: (?&number) | (?&boolean) | (?&string) | (?&array) | (?&object) ) )\n    )';\n\n    /** @var string */\n    private $contents;\n    /** @var string */\n    private $newline;\n    /** @var string */\n    private $indent;\n\n    public function __construct(string $contents)\n    {\n        $contents = trim($contents);\n        if ($contents === '') {\n            $contents = '{}';\n        }\n        if (!Preg::isMatch('#^\\{(.*)\\}$#s', $contents)) {\n            throw new \\InvalidArgumentException('The json file must be an object ({})');\n        }\n        $this->newline = false !== strpos($contents, \"\\r\\n\") ? \"\\r\\n\" : \"\\n\";\n        $this->contents = $contents === '{}' ? '{' . $this->newline . '}' : $contents;\n        $this->detectIndenting();\n    }\n\n    public function getContents(): string\n    {\n        return $this->contents . $this->newline;\n    }\n\n    public function addLink(string $type, string $package, string $constraint, bool $sortPackages = false): bool\n    {\n        $decoded = JsonFile::parseJson($this->contents);\n\n        // no link of that type yet\n        if (!isset($decoded[$type])) {\n            return $this->addMainKey($type, [$package => $constraint]);","sourceCodeStart":30,"sourceCodeEnd":66,"githubUrl":"https://github.com/composer/composer/blob/6ffc1177404d0c50119c22dde6564a380f4a82c9/src/Composer/Json/JsonManipulator.php#L30-L66","documentation":"Thrown by the JsonManipulator constructor when the provided JSON contents, after trimming, do not begin with '{' and end with '}'. JsonManipulator performs surgical regex edits on a JSON *object*; a JSON array, scalar, or malformed content cannot be manipulated, so it rejects non-object input.","triggerScenarios":"Constructing `new JsonManipulator($contents)` where $contents is a JSON array (`[...]`), a scalar, empty-after-trim (handled separately as '{}'), or a fragment that is not a top-level object.","commonSituations":"Passing the body of a 'repositories' array directly instead of the whole composer.json; loading the wrong file (e.g. an array-shaped fixture); corrupted/partial JSON; programmatic callers that assume array input.","solutions":["Ensure the contents passed to JsonManipulator are a full JSON object (`{ ... }`), typically the entire composer.json.","If you have a JSON array, wrap it in an object before manipulating, or edit it with json_decode/json_encode instead.","Re-fetch the file contents and confirm it is the root composer.json, not a sub-document.","Validate with `json_decode($contents)` returning an object before constructing the manipulator."],"exampleFix":"// before\n$contents = file_get_contents('packages.json'); // an array [...]\n$m = new JsonManipulator($contents); // throws\n\n// after\n$contents = file_get_contents('composer.json'); // an object {...}\n$m = new JsonManipulator($contents);","handlingStrategy":"validation","validationCode":"$decoded = json_decode(trim($contents));\nif (!($decoded instanceof \\stdClass)) {\n    throw new \\InvalidArgumentException('Contents must be a JSON object ({...}), got '.gettype($decoded));\n}\n$manipulator = new \\Composer\\Json\\JsonManipulator($contents);","typeGuard":"function isJsonObjectContents(string $contents): bool {\n    $decoded = json_decode(trim($contents));\n    return $decoded instanceof \\stdClass;\n}","tryCatchPattern":"try {\n    $m = new \\Composer\\Json\\JsonManipulator($contents);\n} catch (\\InvalidArgumentException $e) {\n    if (str_contains($e->getMessage(), 'must be an object')) {\n        // load the correct object-shaped file (e.g. composer.json) and retry\n    }\n    throw $e;\n}","preventionTips":["Always pass the entire composer.json object to JsonManipulator, never a sub-array.","Validate the top-level structure with json_decode before constructing the manipulator.","Write tests asserting the manipulated file is an object before edit operations."],"tags":["json","json-manipulator","validation","configuration"],"analyzedSha":"6ffc1177404d0c50119c22dde6564a380f4a82c9","analyzedAt":"2026-08-07T00:01:08.491Z","schemaVersion":2},"datasetVersion":"2026-08-07T02:17:10.218Z"}