◆ await も return もないと次以降のミドルウェアの非同期処理を待たない
◆ 非同期処理以降で設定するレスポンスが未設定なので Not Found になる

これは単純に静的ファイルをサーブする Koa アプリケーションの例です
見つからないパスでは ok という文字を返します

const koa = require("koa")
const app = new koa()

app.use(require("koa-static")("."))

app.use(ctx => {
ctx.body = "ok"
})

app.listen(3001)
"/" にアクセスすると ok が表示される

これにテスト用にいろいろ追加して 一旦なにもしないミドルウェアとして追加したら動かなくなりました

const koa = require("koa")
const app = new koa()

app.use((ctx, next) => {
next()
})

app.use(require("koa-static")("."))

app.use(ctx => {
ctx.body = "ok"
})

app.listen(3001)
"/" にアクセスすると Not Found が表示される

これにしばらく悩まさせられました

追加したミドルウェアは next を呼び出すだけ何もしないものです
もっといろいろごちゃごちゃしてたのに削りに削ってここまで来ても動かないです
ミドルウェア自体を消すと動きます

koa-static のファイルサーブ機能をはずしても何故か動きます

const koa = require("koa")
const app = new koa()

app.use((ctx, next) => {
next()
})

app.use(ctx => {
ctx.body = "ok"
})

app.listen(3001)
"/" にアクセスすると ok が表示される

他で使ってるのと同じスタンダードなミドルウェアと同じように async 関数にして await にしてみたら動きました

const koa = require("koa")
const app = new koa()

app.use(async (ctx, next) => {
await next()
})

app.use(require("koa-static")("."))

app.use(ctx => {
ctx.body = "ok"
})

app.listen(3001)
"/" にアクセスすると ok が表示される

async 関数で Promise を返していても await をしないとダメでした
await で待機しても次にする処理ないのに

なぜかよく分からなかったのですが ちゃんとソースを見て非同期処理の流れを見るとわかりました

解決編

await せず next だけ呼び出すと 次以降のミドルウェアの完了を待たずに Promise が resolve されます
次以降が全て同期的なら良いですが非同期処理が入るとその完了を待機しません
そのせいで Koa がミドルウェアの処理が完了したとみなしたタイミングでは まだ ok がレスポンスボディに設定されていません
その状態でレスポンスを送信するので Not Found が表示されるというわけです

今回の koa-static ではファイルを読み取る非同期処理が入るので ok が設定される前に Not Found でレスポンスが返っていました
koa-static じゃなくても これでも Not Found は再現できます

const koa = require("koa")
const app = new koa()

app.use((ctx, next) => {
next()
})

app.use(async ctx => {
await new Promise(r => setTimeout(r, 1000))
ctx.body = "ok"
})

app.listen(3001)
"/" にアクセスすると Not Found が表示される

サンプルやミドルウェアライブラリのコードを見ていると async 関数じゃないものもあったので await は待機しない限り必須じゃないものだと思い込んでいました
それらのコードではよく見ると next の返り値の Promise を return しています
これでも 次以降のミドルウェアの完了を待機できます

const koa = require("koa")
const app = new koa()

app.use((ctx, next) => {
return next()
})

app.use(require("koa-static")("."))

app.use(ctx => {
ctx.body = "ok"
})

app.listen(3001)
"/" にアクセスすると ok が表示される

単純にそこで処理を終えたいからの return かと思えばちゃんと意味があったんですね

というわけで Koa の next を呼び出す場合は return 返り値をするか await での待機が必須です

一応非同期が入らない場合は ただ next を呼び出すだけでも動きますが 将来的に壊れる可能性が高いのでちゃんと return か await しておくべきです