angular/angular · error · FatalDiagnosticError
DECORATOR_ARG_NOT_LITERAL
DECORATOR_ARG_NOT_LITERAL
Error message
@Service argument must be an object literal
What it means
When @Service is called with one argument, that argument must be a literal object expression. The handler checks ts.isObjectLiteralExpression on decorator.args[0]; a variable reference, string, or any other expression throws DECORATOR_ARG_NOT_LITERAL. Like other Angular annotation handlers, the compiler statically reflects the literal's fields (autoProvided, factory) and cannot evaluate referenced configuration.
Source
Thrown at packages/compiler-cli/src/ngtsc/annotations/src/service.ts:237
const typeArgumentCount = this.reflector.getGenericArityOfClass(clazz) || 0;
if (decorator.args === null) {
throw new FatalDiagnosticError(
ErrorCode.DECORATOR_NOT_CALLED,
decorator.node,
'@Service must be called',
);
}
if (decorator.args.length === 0) {
return {
name,
type,
typeArgumentCount,
};
} else if (decorator.args.length === 1) {
const metaNode = decorator.args[0];
if (!ts.isObjectLiteralExpression(metaNode)) {
throw new FatalDiagnosticError(
ErrorCode.DECORATOR_ARG_NOT_LITERAL,
metaNode,
'@Service argument must be an object literal',
);
}
// Resolve the fields of the literal into a map of field name to expression.
const meta = reflectObjectLiteral(metaNode);
let autoProvided: boolean | undefined;
if (meta.has('autoProvided')) {
const value = meta.get('autoProvided')!;
if (value.kind !== ts.SyntaxKind.TrueKeyword && value.kind !== ts.SyntaxKind.FalseKeyword) {
throw new FatalDiagnosticError(
ErrorCode.VALUE_HAS_WRONG_TYPE,
metaNode,
'`autoProvided` property must be a boolean literal',View on GitHub (pinned to 51cb07e980)
Solutions
- Inline the options: @Service({autoProvided: true})
- Keep the call empty (@Service()) if you have no options to pass
- Ensure the argument is a plain object literal, not a reference, spread, or assertion
Example fix
// before
const opts = {autoProvided: true};
@Service(opts)
export class Cache {}
// after
@Service({autoProvided: true})
export class Cache {} Defensive patterns
Strategy: validation
Validate before calling
import ts from 'typescript';
function serviceArgIsObjectLiteral(dec: ts.Decorator): boolean {
if (!ts.isCallExpression(dec.expression)) return true;
const [arg] = dec.expression.arguments;
return arg === undefined || ts.isObjectLiteralExpression(arg);
} Type guard
function isServiceMetaLiteral(arg: ts.Expression | undefined): arg is ts.ObjectLiteralExpression | undefined {
return arg === undefined || ts.isObjectLiteralExpression(arg);
} Prevention
- Inline @Service options as an object literal; never pass a variable
- Re-use the same mental model as @Component/@NgModule: Angular decorators only accept inline literals
- Avoid spreads and assertions inside decorator arguments
When it happens
Trigger: @Service(SERVICE_OPTS) with a constant defined elsewhere; @Service('name') string form; @Service({...defaults}) spreads or assertion-wrapped values.
Common situations: Centralizing service configuration in constants; codemods extracting decorator options into variables; unfamiliarity with the literal-only requirement shared by all Angular decorators.
Related errors
- DECORATOR_ARG_NOT_LITERAL
- DECORATOR_NOT_CALLED
- DECORATOR_ARITY_WRONG
- DECORATOR_ARITY_WRONG
- DECORATOR_NOT_CALLED
AI-assisted analysis of angular/angular@51cb07e980 (2026-08-22).
Data as JSON: /api/errors/71326c368b757f25.
Report an issue: GitHub.