You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
marketplace: when the catalog host is unreachable the error panel shows the raw "fetch failed" and reads the 502 as "the catalog returned an error", pointing the admin at the wrong cause #25891
Filing gate ① a user-visible defect with a named landing site. Batch-of: 2026-10-25 browser pass on @objectstack/* 18.1.0 (23 cards: hotcrm 10, objectui 10, objectstack 3). Filed by the maintainer's account (os-steve) from the HotCRM pass driven in session session_01AwpXhq2nFKU6J7GmbCEmZu; ⛔ not a claim. Grade and routing are objectui triage's.
What is seen (@objectstack/console 18.1.0, a runtime whose outbound network is blocked)
Setup → 浏览应用市场: GET /api/v1/marketplace/packages?limit=100 answers 502 (the runtime could not reach the catalog host). The panel reads:
Two things mislead: the catalog did not "return an error", the runtime's fetch to it failed (the third line says so, in the raw Node error string); and "fetch failed" is a developer string on an admin page. t03-应用市场.png.
reach: the public UI, measured once as the dev admin on an intranet-style sandbox; every air-gapped or proxied deployment meets it.
Expected
The runtime's marketplace proxy tells upstream-unreachable apart from upstream-error (it knows: fetch failed vs a response), and the panel says 「运行时无法连接应用市场目录()。请检查运行时的出网或代理设置。」 with the raw error behind a 「详情」 toggle (T2-U07's rule: one plain sentence, details behind More).
Transferred 2026-10-26T02:53Z from objectstack-ai/objectui#13523, with its comments and timeline; the old URL redirects here. objectui moved into this repository as the ui/ workspace (epic #25677) and is being archived (#25854). The body is left exactly as filed.
Paths. The body names no file paths; the UI code it describes lives under ui/ here (the Console is ui/apps/console, the libraries ui/packages/*).
Claim: PM loop round 3 · 2026-10-26T15:00Z
Session: session_01SMps7wq7rBTk53e8KTSEup
Account: os-steve
Branch: claude/issue-25891-marketplace-unreachable-copy
Worktree: objectstack-issue-25891
Domain: domain:ui
Seat: domain:ui#2
File surface: ui/packages/app-shell/src/console/marketplace/MarketplacePage.tsx (LoadFailure / toLoadFailure and the load-error panel only), ui/packages/app-shell/src/console/marketplace/__tests__/MarketplacePage.loadFailureCause-11688.test.tsx (flip the proxy-502 case, add a control), root .changeset/25891-marketplace-unreachable-copy.md. Stop on breach and explain it in the report. ⛔ ui/packages/i18n/src/locales/*.ts (reuse existing keys only), marketplaceApi.ts, packages/cloud-connection/**, ui/.github/** and the root .github/workflows/ci.yml are out of bounds. scripts/sdui-manifest.* stays untouched, because app-shell is outside the registry closure.
Container & model: S, mode:subagent, model: default tier, effort: medium
Clause-②: no
Metadata-protocol: none
Responsibility: the marketplace load-error panel, which picks its sentence by HTTP status alone | the proxy's own MARKETPLACE_PROXY_FAILED code, already on the wire and already stamped on the error by marketplaceApi call() | every admin whose runtime cannot reach the catalog host
Thread-read: 6407169053
Premise: corrected 2026-10-26T14:58Z — on origin/main83a26c38d0:
The proxy answers 502 MARKETPLACE_PROXY_FAILED only when its own fetch throws (packages/cloud-connection/src/marketplace-proxy-plugin.ts:355-367); upstream answers pass through with their own status.
marketplaceApi.ts:219-226 already sets err.code.
MarketplacePage.tsx:171-178 toLoadFailure never reads code, and :720-723 picks answeredError for any status. A proxy 502 therefore reads "the catalog returned an error (HTTP 502)", and :726 prints the raw "fetch failed" in full view.
The card's literal copy, which names the host, needs a new server wire field and new pack keys. It is out of this claim and becomes a follow-up if wanted.
Serial constraints cleared: none. No open claim holds MarketplacePage.tsx. Open PR feat(app-shell,i18n): the Console draws the DEPENDENCY_MISSING install refusal in the author's terms #26123 edits other regions of the same file (import, a hook, installLocal), so rebase if it lands first.
Writes: PR only via the relay op pr_create; comments only via the relay write tools; ⛔ no GitHub MCP writes.
Ruling section: triage 6407161741 says "the 502 relay's code should drive the copy".
Seat's route:
Carry code on LoadFailure. Treat code === 'MARKETPLACE_PROXY_FAILED' (and no status) as the unreachable class, with the line marketplace.load.unreachable. For that class only, put the raw cause behind a More/Less disclosure that reuses refusalDetails.more/less/copy.
Answered refusals (403, and a 502/503/504 relayed from upstream) keep answeredError and their visible server text (objectui#11688).
The online/same-origin hint logic is unchanged (the sameOriginHint-11726 test stays green).
Tests: flip the 11688 proxy-502 case (unreachable line, then open the disclosure, then 'fetch failed'), and add a 503 control and a 403 control.
{
"issue": 25891,
"status": "done",
"branch": "claude/issue-25891-marketplace-unreachable-copy",
"pr": "https://github.com/objectstack-ai/objectstack/pull/26213",
"pr_number": 26213,
"head_sha": "a7b293f0868d9c100b62a1803d0d3fcb2ba8e2a5",
"session": "session_01SMps7wq7rBTk53e8KTSEup",
"premise_still_valid": true,
"summary": "Verified the claim's premise at main 7111e14a: the proxy answers 502 MARKETPLACE_PROXY_FAILED only when its own fetch throws, and call() already stamps err.code. toLoadFailure never read the code, and the panel picked its class line by status alone. LoadFailure now carries code plus an unreachable class (no status, or code MARKETPLACE_PROXY_FAILED). That class reads marketplace.load.unreachable, and its raw transport text sits behind a More/Less disclosure with a copy button, built from the existing refusalDetails.more/less/copy/copied keys. Answered failures (403, and a 502/503/504 relayed from upstream) keep answeredError with their visible server text, and the online/same-origin hint logic is unchanged. No locale pack, marketplaceApi.ts, cloud-connection, .github or sdui-manifest file is touched. The card assignee (os-steve) was left untouched and is mirrored on the PR.",
"tests": "At head a7b293f0, under os-verify-lock: `cd ui && pnpm exec vitest run --maxWorkers=2 packages/app-shell/src/console/marketplace/__tests__/MarketplacePage` gave VERDICT command-exit 0 with Test Files 9 passed (9) and Tests 67 passed (67), including sameOriginHint-11726 and loadingStates-12710 unchanged. `pnpm --filter @object-ui/app-shell run type-check` (tsc --noEmit plus tsconfig.test.json) gave VERDICT command-exit 0 with 0 TS errors. eslint on the 2 changed files exited 0 with 0 errors and 3 warnings, all on pre-existing lines (414, 421, 506). Ablation at head 24315db2 used scripts/ablation-replace.mjs to turn `unreachable: status === undefined || code === PROXY_UNREACHABLE_CODE,` into `unreachable: status === undefined,`. Anchor went x1 to x0 and blob f19da1750ab1 to 46ac6aa14f87. The 11688 suite then had 1 failed and 9 passed, the failing case being exactly the flipped proxy-502 case. Restore checked out: blob == HEAD and git diff HEAD empty. No dist rebuild was needed because the suite imports MarketplacePage from src. Prerequisite: the CI 'Build UI Deps' filter set and the app-shell^... closure were built through turbo, all cache hits.",
"gates": "Local, at head a7b293f0 after the last commit: `node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack` derived 35 commands from 3 paths, +161/-20 = 181 changed lines against the 3000 threshold. All 35 were run, each exiting 0 with the exit code captured before any pipe. They include check-sdui-manifest.mjs (exit 0), check-adr-0087-registration --base origin/main, check-changeset-no-major, check-empty-changeset, check:cross-package-test-inputs, check:test-source-alias, check:type-check-coverage, check:type-check-debt, check:published-files and check:nul-bytes. The `--ran` reconciliation found 35 derived, 35 run, 0 NOT-MEASURED, 0 UNRUN, a derived zero with exit codes recorded. The roster gate flagged for .changeset/, `node scripts/check-changeset-fixed.mjs`, exited 0. CI families (Type Check UI, Test UI 16 shards, Lint UI, Lint & Repo Gates, Check Changeset) are in_progress or not yet read on PR #26213. Per contract the report does not wait on them.",
"line_budget": "+161 / -20 = 181 changed lines over 3 files (MarketplacePage.tsx +99/-20, i.e. 119 lines; test +50; changeset +12). That is under the 3000 human-merge threshold. The claim names no line ceiling.",
"files_changed": [
"ui/packages/app-shell/src/console/marketplace/MarketplacePage.tsx",
"ui/packages/app-shell/src/console/marketplace/__tests__/MarketplacePage.loadFailureCause-11688.test.tsx",
".changeset/25891-marketplace-unreachable-copy.md"
],
"deviations": [
"Commit trailers use the model-free pair from AGENTS.md (Claude-Session plus Co-authored-by: Claude) instead of the harness reminder's model-named Co-Authored-By. AGENTS.md takes precedence, and the pre-push hook refuses a model identifier.",
"Merged origin/main (4 unrelated commits, no overlap with the file surface) into the branch before opening the PR. The first dispatch-gates derivation warned STALE TREE, and the gates were then re-derived and run on the merged head.",
"Beyond 'flip the proxy-502 case plus 503/403 controls', the existing no-answer case (TypeError Failed to fetch) got 2 extra assertions. The no-status class also moved its raw text behind More, and that behaviour change needed a pin.",
"The copy button's confirmed state uses the existing refusalDetails.copied key, alongside more/less/copy, matching RefusalMessage. No new keys were added.",
"The ablation ran at 24315db2 rather than the final merge head a7b293f0. The merge brought only unrelated commits, and the full MarketplacePage suites were re-run green at a7b293f0.",
"test:repo (vitest repo project) was not run locally. It belongs to CI's Test Core."
],
"dogfood_battery": "n/a: the PR title has no `!`, and Clause-② is `no` with no (narrowing) arm",
"mcp_calls": "0",
"api_writes": "2: (1) fleet-write relay stroke fw-20261026T154801Z-27b8be, POST /repos/objectstack-ai/objectstack/dispatches (204). It ran as run 40352876457, which executed POST /repos/objectstack-ai/objectstack/pulls (draft #26213) and POST /repos/objectstack-ai/objectstack/issues/26213/assignees [os-steve], read back byte-identical (4651/4651). (2) This os-dev-report comment via scripts/pm/post-stamped.mjs, POST /repos/objectstack-ai/objectstack/issues/25891/comments. git push is not counted as a REST write.",
"open_questions": [
{
"question": "The unreachable disclosure is a local LoadFailureCause component in MarketplacePage.tsx that mirrors RefusalMessage's More/Less/copy markup. RefusalMessage could not be reused: it splits a single server text into lead and rest, while here the lead is our own sentence. Should RefusalMessage grow a lead prop so both share one disclosure?",
"options": [
"A: keep the local component (as shipped; RefusalMessage.tsx was outside this claim's file surface)",
"B: a follow-up adds an optional lead prop to RefusalMessage and MarketplacePage switches to it, removing roughly 40 duplicated lines"
],
"recommendation": "B as a small follow-up if a third surface needs the same lead-plus-raw-detail shape. A is fine until then."
}
],
"out_of_scope_findings": [
"carrier: none (noted in PR Acceptance notes only). The card's literal copy names the catalog host. That needs a new structured host field on the proxy's MARKETPLACE_PROXY_FAILED envelope (packages/cloud-connection) plus new pack keys, and the claim's Premise already rules it out of scope as a follow-up if wanted. Dedupe words: marketplace proxy host field, MARKETPLACE_PROXY_FAILED host, unreachable catalog host copy."
]
}
Status: an unreachable catalog host now reads as unreachable (no status, or MARKETPLACE_PROXY_FAILED), and the failure carries a More/Less cause disclosure with a copy button. pm:dispatched removed.
Activity
objectstack-fleet commentedon Oct 25, 2026
Triage: first grade —
bug·priority:p3·domain:ui·area:devpath·pm:queue· triage seat ·session_01AavokzJ5DndAwitDXvKy4U· 2026-10-25T12:59Z⛔ Not a claim, ⛔ not a dispatch.
findingcomes off.Generated by Claude Code
objectstack-fleet commentedon Oct 26, 2026
Transferred 2026-10-26T02:53Z from objectstack-ai/objectui#13523, with its comments and timeline; the old URL redirects here. objectui moved into this repository as the
ui/workspace (epic #25677) and is being archived (#25854). The body is left exactly as filed.Paths. The body names no file paths; the UI code it describes lives under
ui/here (the Console isui/apps/console, the librariesui/packages/*).References. The bare issue numbers in the body were written in objectui and now resolve against this repository by mistake; read them as objectstack-ai/objectui#5557, objectstack-ai/objectui#11627, objectstack-ai/objectui#11688, objectstack-ai/objectui#11726, objectstack-ai/objectui#12097.
No stale
pm:dispatchedor assignee to strip.Generated by Claude Code
objectstack-fleet commentedon Oct 26, 2026
Claim: PM loop round 3 · 2026-10-26T15:00Z
Session:
session_01SMps7wq7rBTk53e8KTSEupAccount:
os-steveBranch:
claude/issue-25891-marketplace-unreachable-copyWorktree:
objectstack-issue-25891Domain:
domain:uiSeat:
domain:ui#2File surface:
ui/packages/app-shell/src/console/marketplace/MarketplacePage.tsx(LoadFailure/toLoadFailureand the load-error panel only),ui/packages/app-shell/src/console/marketplace/__tests__/MarketplacePage.loadFailureCause-11688.test.tsx(flip the proxy-502 case, add a control), root.changeset/25891-marketplace-unreachable-copy.md. Stop on breach and explain it in the report. ⛔ui/packages/i18n/src/locales/*.ts(reuse existing keys only),marketplaceApi.ts,packages/cloud-connection/**,ui/.github/**and the root.github/workflows/ci.ymlare out of bounds.scripts/sdui-manifest.*stays untouched, because app-shell is outside the registry closure.Container & model:
S,mode:subagent,model: default tier,effort: mediumClause-②: no
Metadata-protocol: none
Responsibility:
the marketplace load-error panel, which picks its sentence by HTTP status alone|the proxy's own MARKETPLACE_PROXY_FAILED code, already on the wire and already stamped on the error by marketplaceApi call()|every admin whose runtime cannot reach the catalog hostThread-read: 6407169053
Premise: corrected 2026-10-26T14:58Z — on
origin/main83a26c38d0:The proxy answers 502
MARKETPLACE_PROXY_FAILEDonly when its own fetch throws (packages/cloud-connection/src/marketplace-proxy-plugin.ts:355-367); upstream answers pass through with their own status.marketplaceApi.ts:219-226already setserr.code.MarketplacePage.tsx:171-178 toLoadFailurenever readscode, and:720-723picksansweredErrorfor any status. A proxy 502 therefore reads "the catalog returned an error (HTTP 502)", and:726prints the raw "fetch failed" in full view.The card's literal copy, which names the host, needs a new server wire field and new pack keys. It is out of this claim and becomes a follow-up if wanted.
Serial constraints cleared: none. No open claim holds
MarketplacePage.tsx. Open PR feat(app-shell,i18n): the Console draws the DEPENDENCY_MISSING install refusal in the author's terms #26123 edits other regions of the same file (import, a hook,installLocal), so rebase if it lands first.Writes: PR only via the relay op
pr_create; comments only via the relay write tools; ⛔ no GitHub MCP writes.Ruling section: triage 6407161741 says "the 502 relay's code should drive the copy".
Seat's route:
codeonLoadFailure. Treatcode === 'MARKETPLACE_PROXY_FAILED'(and no status) as the unreachable class, with the linemarketplace.load.unreachable. For that class only, put the raw cause behind a More/Less disclosure that reusesrefusalDetails.more/less/copy.answeredErrorand their visible server text (objectui#11688).sameOriginHint-11726test stays green).Fixes #25891.objectstack-fleet commentedon Oct 26, 2026
os-dev-report
{ "issue": 25891, "status": "done", "branch": "claude/issue-25891-marketplace-unreachable-copy", "pr": "https://github.com/objectstack-ai/objectstack/pull/26213", "pr_number": 26213, "head_sha": "a7b293f0868d9c100b62a1803d0d3fcb2ba8e2a5", "session": "session_01SMps7wq7rBTk53e8KTSEup", "premise_still_valid": true, "summary": "Verified the claim's premise at main 7111e14a: the proxy answers 502 MARKETPLACE_PROXY_FAILED only when its own fetch throws, and call() already stamps err.code. toLoadFailure never read the code, and the panel picked its class line by status alone. LoadFailure now carries code plus an unreachable class (no status, or code MARKETPLACE_PROXY_FAILED). That class reads marketplace.load.unreachable, and its raw transport text sits behind a More/Less disclosure with a copy button, built from the existing refusalDetails.more/less/copy/copied keys. Answered failures (403, and a 502/503/504 relayed from upstream) keep answeredError with their visible server text, and the online/same-origin hint logic is unchanged. No locale pack, marketplaceApi.ts, cloud-connection, .github or sdui-manifest file is touched. The card assignee (os-steve) was left untouched and is mirrored on the PR.", "tests": "At head a7b293f0, under os-verify-lock: `cd ui && pnpm exec vitest run --maxWorkers=2 packages/app-shell/src/console/marketplace/__tests__/MarketplacePage` gave VERDICT command-exit 0 with Test Files 9 passed (9) and Tests 67 passed (67), including sameOriginHint-11726 and loadingStates-12710 unchanged. `pnpm --filter @object-ui/app-shell run type-check` (tsc --noEmit plus tsconfig.test.json) gave VERDICT command-exit 0 with 0 TS errors. eslint on the 2 changed files exited 0 with 0 errors and 3 warnings, all on pre-existing lines (414, 421, 506). Ablation at head 24315db2 used scripts/ablation-replace.mjs to turn `unreachable: status === undefined || code === PROXY_UNREACHABLE_CODE,` into `unreachable: status === undefined,`. Anchor went x1 to x0 and blob f19da1750ab1 to 46ac6aa14f87. The 11688 suite then had 1 failed and 9 passed, the failing case being exactly the flipped proxy-502 case. Restore checked out: blob == HEAD and git diff HEAD empty. No dist rebuild was needed because the suite imports MarketplacePage from src. Prerequisite: the CI 'Build UI Deps' filter set and the app-shell^... closure were built through turbo, all cache hits.", "gates": "Local, at head a7b293f0 after the last commit: `node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack` derived 35 commands from 3 paths, +161/-20 = 181 changed lines against the 3000 threshold. All 35 were run, each exiting 0 with the exit code captured before any pipe. They include check-sdui-manifest.mjs (exit 0), check-adr-0087-registration --base origin/main, check-changeset-no-major, check-empty-changeset, check:cross-package-test-inputs, check:test-source-alias, check:type-check-coverage, check:type-check-debt, check:published-files and check:nul-bytes. The `--ran` reconciliation found 35 derived, 35 run, 0 NOT-MEASURED, 0 UNRUN, a derived zero with exit codes recorded. The roster gate flagged for .changeset/, `node scripts/check-changeset-fixed.mjs`, exited 0. CI families (Type Check UI, Test UI 16 shards, Lint UI, Lint & Repo Gates, Check Changeset) are in_progress or not yet read on PR #26213. Per contract the report does not wait on them.", "line_budget": "+161 / -20 = 181 changed lines over 3 files (MarketplacePage.tsx +99/-20, i.e. 119 lines; test +50; changeset +12). That is under the 3000 human-merge threshold. The claim names no line ceiling.", "files_changed": [ "ui/packages/app-shell/src/console/marketplace/MarketplacePage.tsx", "ui/packages/app-shell/src/console/marketplace/__tests__/MarketplacePage.loadFailureCause-11688.test.tsx", ".changeset/25891-marketplace-unreachable-copy.md" ], "deviations": [ "Commit trailers use the model-free pair from AGENTS.md (Claude-Session plus Co-authored-by: Claude) instead of the harness reminder's model-named Co-Authored-By. AGENTS.md takes precedence, and the pre-push hook refuses a model identifier.", "Merged origin/main (4 unrelated commits, no overlap with the file surface) into the branch before opening the PR. The first dispatch-gates derivation warned STALE TREE, and the gates were then re-derived and run on the merged head.", "Beyond 'flip the proxy-502 case plus 503/403 controls', the existing no-answer case (TypeError Failed to fetch) got 2 extra assertions. The no-status class also moved its raw text behind More, and that behaviour change needed a pin.", "The copy button's confirmed state uses the existing refusalDetails.copied key, alongside more/less/copy, matching RefusalMessage. No new keys were added.", "The ablation ran at 24315db2 rather than the final merge head a7b293f0. The merge brought only unrelated commits, and the full MarketplacePage suites were re-run green at a7b293f0.", "test:repo (vitest repo project) was not run locally. It belongs to CI's Test Core." ], "dogfood_battery": "n/a: the PR title has no `!`, and Clause-② is `no` with no (narrowing) arm", "mcp_calls": "0", "api_writes": "2: (1) fleet-write relay stroke fw-20261026T154801Z-27b8be, POST /repos/objectstack-ai/objectstack/dispatches (204). It ran as run 40352876457, which executed POST /repos/objectstack-ai/objectstack/pulls (draft #26213) and POST /repos/objectstack-ai/objectstack/issues/26213/assignees [os-steve], read back byte-identical (4651/4651). (2) This os-dev-report comment via scripts/pm/post-stamped.mjs, POST /repos/objectstack-ai/objectstack/issues/25891/comments. git push is not counted as a REST write.", "open_questions": [ { "question": "The unreachable disclosure is a local LoadFailureCause component in MarketplacePage.tsx that mirrors RefusalMessage's More/Less/copy markup. RefusalMessage could not be reused: it splits a single server text into lead and rest, while here the lead is our own sentence. Should RefusalMessage grow a lead prop so both share one disclosure?", "options": [ "A: keep the local component (as shipped; RefusalMessage.tsx was outside this claim's file surface)", "B: a follow-up adds an optional lead prop to RefusalMessage and MarketplacePage switches to it, removing roughly 40 duplicated lines" ], "recommendation": "B as a small follow-up if a third surface needs the same lead-plus-raw-detail shape. A is fine until then." } ], "out_of_scope_findings": [ "carrier: none (noted in PR Acceptance notes only). The card's literal copy names the catalog host. That needs a new structured host field on the proxy's MARKETPLACE_PROXY_FAILED envelope (packages/cloud-connection) plus new pack keys, and the claim's Premise already rules it out of scope as a follow-up if wanted. Dedupe words: marketplace proxy host field, MARKETPLACE_PROXY_FAILED host, unreachable catalog host copy." ] }Generated by Claude Code
objectstack-fleet commentedon Oct 26, 2026
Landed · seat 2 · 2026-10-26T18:06Z
b239a01a, head7949e939(+161/−20)origin/main9598405e, containsb239a01a(git merge-base --is-ancestorexit 0)gh-readonly-queue/main/pr-26213-*: none left (consumed)Fixes #25891on #26213Status: an unreachable catalog host now reads as unreachable (no status, or
MARKETPLACE_PROXY_FAILED), and the failure carries a More/Less cause disclosure with a copy button.pm:dispatchedremoved.