Skip to content

Carry the vendor's reason, and send client_id where the trade needs it - #27

Merged
fajarhide merged 1 commit into
mainfrom
fix/token-error-body
Aug 17, 2026
Merged

Carry the vendor's reason, and send client_id where the trade needs it#27
fajarhide merged 1 commit into
mainfrom
fix/token-error-body

Conversation

@fajarhide

Copy link
Copy Markdown
Owner

Two defects found on the first real Facebook connection. Both are older than that provider and neither is in it.

The reason was being thrown away. Every failure path threw a status and dropped the body, on the token helpers and on the manifest executor alike. The first connection died on this and stayed dead:

{"error":{"code":"upstream_error","message":"token endpoint returned 400"}}

True and useless, and expensive: the code is single use and the state row is gone by then, so every diagnosis attempt costs another consent round trip. With the body carried, the next attempt answered immediately:

token endpoint returned 400: {"error":{"message":"Missing client_id parameter.","type":"OAuthException","code":101}}

and so did the tool calls that failed after it, which turned out to be Meta refusing the endpoint rather than anything this gateway did:

facebook returned 400: {"error":{"message":"Permissions error","code":200,"error_subcode":2069030,
"error_user_title":"New Pages Experience Is Not Supported"}}

Read on the failure path only, since a successful body holds the tokens, and capped at 400 characters so a vendor answering with an HTML error page cannot flood a log line.

tradeUp never sent client_id. It sent grant_type, the token and client_secret. Threads does not need the app id, Facebook refuses the call without it, and it refuses after the code exchange has already succeeded, so the consent is spent and the connection ends with no grant.

Verified against the vendor with a deliberately invalid token, so nothing real was exchanged:

without client_id -> {"message":"Missing client_id parameter.","code":101}
with client_id    -> {"message":"Invalid OAuth access token - Cannot parse access token","code":190}

The second is the endpoint accepting the request and rejecting the fake token.

It is a flag rather than always-on because the two Meta trades genuinely differ, and Threads has never been run against its vendor, so changing what it sends would be a guess stacked on a guess.

After both, a real Facebook connection completes and get_me returns real data.

$ npx vitest run
Test Files  36 passed (36)
     Tests  368 passed (368)

Closes #23
Closes #24

…rade needs it

Two defects found on the first real Facebook connection, both older than that
provider and neither of them in it.

Every failure path threw a status and dropped the body, on the token helpers
and on the manifest executor alike. So the first connection died on "token
endpoint returned 400" and stayed dead: the code is single use and the state
row is gone by then, so each diagnosis attempt cost another consent. With the
body carried, the next attempt named the cause at once, and so did the two tool
calls that failed after it. Read on the failure path only, since a successful
body holds the tokens, and bounded so an HTML error page cannot flood a log.

tradeUp sent grant_type, the token and client_secret, and never the app id.
Threads does not need it; Facebook refuses the call without it, after the code
exchange has already succeeded. Verified against the vendor with an invalid
token so nothing real was exchanged: without client_id "Missing client_id
parameter", with it "Cannot parse access token", which is the endpoint
accepting the request and rejecting the fake token. A flag rather than always,
because the two Meta trades genuinely differ and Threads has never been run
against its vendor.

Closes #23
Closes #24
@fajarhide
fajarhide merged commit 8674863 into main Aug 17, 2026
2 checks passed
@fajarhide
fajarhide deleted the fix/token-error-body branch August 17, 2026 06:15
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

1 participant