evanw/esbuild · error · LexerPanic
Unexpected newline after "async"
Error message
Unexpected newline after "async"
What it means
In 'export async function ...', automatic semicolon insertion is not permitted between 'async' and 'function'. esbuild checks HasNewlineBefore after consuming 'async' (js_parser.go:7166) and, if a line break precedes 'function', errors.
Source
Thrown at internal/js_parser/js_parser.go:7169
}
if p.lexer.IsContextualKeyword("as") {
// "export as namespace ns;"
p.lexer.Next()
p.lexer.ExpectContextualKeyword("namespace")
p.lexer.Expect(js_lexer.TIdentifier)
p.lexer.ExpectOrInsertSemicolon()
return js_ast.Stmt{Loc: loc, Data: js_ast.STypeScriptShared}
}
if p.lexer.IsContextualKeyword("async") {
// "export async function foo() {}"
asyncRange := p.lexer.Range()
p.lexer.Next()
if p.lexer.HasNewlineBefore {
p.log.AddError(&p.tracker, logger.Range{Loc: logger.Loc{Start: asyncRange.End()}},
"Unexpected newline after \"async\"")
panic(js_lexer.LexerPanic{})
}
p.lexer.Expect(js_lexer.TFunction)
opts.isExport = true
return p.parseFnStmt(loc, opts, true /* isAsync */, asyncRange)
}
if p.options.ts.Parse {
switch p.lexer.Identifier.String {
case "type":
// "export type foo = ..."
typeRange := p.lexer.Range()
p.lexer.Next()
if p.lexer.HasNewlineBefore && p.lexer.Token != js_lexer.TOpenBrace && p.lexer.Token != js_lexer.TAsterisk {
p.log.AddError(&p.tracker, logger.Range{Loc: logger.Loc{Start: typeRange.End()}},
"Unexpected newline after \"type\"")
panic(js_lexer.LexerPanic{})
}
p.skipTypeScriptTypeStmt(parseStmtOpts{isModuleScope: opts.isModuleScope, isExport: true})View on GitHub (pinned to f6058f8364)
Solutions
- Keep 'async function' together with no line break between them.
- Reformat so 'export async function' sits on one line (or wrap elsewhere).
Example fix
// before
export async
function foo() {}
// after
export async function foo() {} Defensive patterns
Strategy: validation
Validate before calling
import "github.com/evanw/esbuild/pkg/api"
res := api.Transform(src, api.TransformOptions{Loader: api.LoaderJS})
for _, m := range res.Errors {
if strings.Contains(m.Text, `Unexpected newline after "async"`) {
// a line break sits between 'async' and 'function' in an export
}
} Prevention
- Keep 'async function' on one line in export declarations.
- Run tsc/eslint to catch ASI-incompatible line breaks.
- Configure prettier not to break between 'async' and 'function'.
When it happens
Trigger: 'export async\nfunction foo() {}' — a line break between 'async' and 'function' in an exported async function declaration.
Common situations: A formatter/prettier line-wrapping the keyword onto its own line; manual edits splitting the declaration.
Related errors
- Unexpected newline after "type"
- Unexpected "interface"
- Unexpected newline before "=>"
- For loop initializers cannot start with "async of"
- Invalid binding pattern
AI-assisted analysis of evanw/esbuild@f6058f8364 (2026-08-09).
Data as JSON: /api/errors/615f32fa1e9f7274.
Report an issue: GitHub.