Skip to content

[native] possible #484 follow-up: "expected number" abort in large httpServe handler after await fetch (dune-server Hypery /oauth/token) #686

Description

@spacedevin

Summary

Production hit in dune-ide dune-server (native --feature http,http-hyper,…): after a successful browser Hypery PKCE login, the server aborted on the /oauth/token exchange path with:

thread 'main' panicked at src/main.rs:…:
expected number

This looks like the same f64 mis-promotion / from_value_expr(F64) class as #484 (non-number value live across await), but #484 is closed as fixed and a small standalone repro on current tip (v3.8.6 / 0.0.0-dev from main) does not panic.

Filing as a follow-up / incomplete-coverage report so the production shape is tracked.

Production shape (dune-server)

Handler sketch (pre-workaround) inside an httpServe / hyper callback — strings derived from the request body, used in an object after await fetch + await resp.text():

async fn handle(req) {
  let body = JSON.parse(typeof req.body === "string" ? req.body : "")
  let clientId = typeof body.clientId === "string" ? body.clientId.trim() : ""
  let web = typeof body.web === "string" ? body.web.trim() : "https://hypery.ai"
  while (web.length > 0 && web[web.length - 1] === "/") {
    web = web.slice(0, web.length - 1)
  }
  let form = "grant_type=authorization_code&client_id=" + encodeURIComponent(clientId) /* + … */
  let resp = await fetch(web + "/api/oauth/token", {
    method: "POST",
    headers: { "Content-Type": "application/x-www-form-urlencoded", "Accept": "application/json" },
    body: form
  })
  let text = await resp.text()
  let v = JSON.parse(text)
  // … validate access_token …
  return {
    ok: true,
    access_token: v.access_token,
    web_base: web,        // ← string local after await
    client_id: clientId   // ← string local after await
  }
}

In the large dune-server binary this aborted with expected number right as login completed (browser had already returned from Hypery authorize → /callback).

Workaround applied in dune-ide

Pin copies before the await and only use the pinned names after (same defensive pattern dune already uses around hypery_embed / tish#484 notes):

let webOut = "" + web
let clientIdOut = "" + clientId
let resp = await fetch(/* … */)
// …
return { web_base: webOut, client_id: clientIdOut, /* … */ }

What we tried on tip (does NOT panic)

On /Users/…/tish/target/release/tish (tish 0.0.0-dev, describes near v3.8.6):

  1. Minimal [native] non-number param crossing an await in an async fn is mis-promoted to f64 → panic!("expected number") #484-style async fn f(s){ await …; return s }not re-run here; assume still fixed per fix(native): cover S-0 object-field-value params in the numeric-arg whitelist (#484) #490.
  2. Standalone await fetch + use web/clientId locals after await (same field extraction as above, invalid auth code so Hypery returns an error body) — completes, returns JSON error, no panic.
  3. Classic webGet(apiUrl, apiKey, path) with apiKey used after await — OK on tip.

So this may only surface under whole-program inference of a large native binary (dune-server pulls in host-commands + serveWorkspace + agent pump), or via a slightly different value flow than the isolated case.

Ask

Refs

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions