{"record":{"id":"537f5145e63c8e66","repo":"mongodb/laravel-mongodb","slug":"the-stage-name-s-is-invalid-it-must-start-with-a-sign","errorCode":null,"errorMessage":"The stage name \"%s\" is invalid. It must start with a \"$\" sign.","messagePattern":"The stage name \"(.+?)\" is invalid\\. It must start with a \"\\$\" sign\\.","errorType":"exception","errorClass":"InvalidArgumentException","httpStatus":null,"severity":"error","filePath":"src/Query/AggregationBuilder.php","lineNumber":38,"sourceCode":"\nclass AggregationBuilder\n{\n    use FluentFactoryTrait;\n\n    public function __construct(\n        private Collection $collection,\n        private readonly array $options = [],\n    ) {\n    }\n\n    /**\n     * Add a stage without using the builder. Necessary if the stage is built\n     * outside the builder, or it is not yet supported by the library.\n     */\n    public function addRawStage(string $operator, mixed $value): static\n    {\n        if (! str_starts_with($operator, '$')) {\n            throw new InvalidArgumentException(sprintf('The stage name \"%s\" is invalid. It must start with a \"$\" sign.', $operator));\n        }\n\n        $this->pipeline[] = [$operator => $value];\n\n        return $this;\n    }\n\n    /**\n     * Execute the aggregation pipeline and return the results.\n     */\n    public function get(array $options = []): LaravelCollection|LazyCollection\n    {\n        $cursor = $this->execute($options);\n\n        return collect($cursor->toArray());\n    }\n\n    /**","sourceCodeStart":20,"sourceCodeEnd":56,"githubUrl":"https://github.com/mongodb/laravel-mongodb/blob/0634653039468ceb0268a69192bdac64469ed043/src/Query/AggregationBuilder.php#L20-L56","documentation":"AggregationBuilder::addRawStage() appends a raw pipeline stage, and MongoDB pipeline stage names must begin with '$'. This guard rejects operator strings missing the leading dollar sign before they can produce an invalid aggregation pipeline.","triggerScenarios":"Calling ->addRawStage('match', [...]) or any operator string without a leading '$', e.g. 'group', 'project', 'lookup'.","commonSituations":"Developers coming from SQL-ish builder APIs forget the '$' prefix, or paste a stage name from documentation that lists stages without the '$'.","solutions":["Prefix the operator with '$': addRawStage('$match', $filter).","For full typed stage building, use the fluent aggregation builder methods (match(), group(), etc.) instead of addRawStage.","Check the stage name against MongoDB's aggregation pipeline stage reference."],"exampleFix":"// before\n$builder->addRawStage('match', ['status' => 'active']);\n// after\n$builder->addRawStage('$match', ['status' => 'active']);","handlingStrategy":"validation","validationCode":"if (! str_starts_with($operator, '$')) {\n    throw new InvalidArgumentException('Stage must start with $');\n}\n$builder->addRawStage($operator, $value);","typeGuard":"function isValidStageName(string $op): bool { return str_starts_with($op, '$'); }","tryCatchPattern":"try {\n    $builder->addRawStage($operator, $value);\n} catch (InvalidArgumentException $e) {\n    if (str_contains($e->getMessage(), 'must start with a \"$\" sign')) {\n        $operator = '$' . ltrim($operator, '$');\n        $builder->addRawStage($operator, $value);\n    } else { throw $e; }\n}","preventionTips":["Always write raw stage operators with the '$' prefix ('match', 'group')","Prefer built-in fluent stage methods over addRawStage when available","Keep a list of valid MongoDB stage names in your linter/tests","Add unit tests asserting stage names start with '$'"],"tags":["aggregation","pipeline","validation"],"backgroundTag":"invalid-argument-format","analyzedSha":"0634653039468ceb0268a69192bdac64469ed043","analyzedAt":"2026-09-15T02:56:37.067Z","contentChangedAt":"2026-09-15T02:56:37.067Z","schemaVersion":2},"datasetVersion":"2026-09-16T09:17:16.951Z"}