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

  1. Keep 'async function' together with no line break between them.
  2. 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

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


AI-assisted analysis of evanw/esbuild@f6058f8364 (2026-08-09). Data as JSON: /api/errors/615f32fa1e9f7274. Report an issue: GitHub.