Skip to content
This repository was archived by the owner on Nov 4, 2026. It is now read-only.
This repository was archived by the owner on Nov 4, 2026. It is now read-only.

Settings save: render the dedicated SETTINGS_CRYPTO_UNAVAILABLE refusal instead of the generic error path #4570

Closed

Description

@hotlong

Follow-up finding from objectstack-ai/objectstack#8273 (PR objectstack-ai/objectstack#8396), filed unassigned for triage.

As of that PR, the framework's fail-closed settings refusal — a declared-encrypted value (encrypted: true / manifest type: 'password') refused because the deployment has nothing able to encrypt it — answers PUT /api/settings/:namespace with a dedicated wire envelope instead of the generic one:

  • status: 500 (unchanged)
  • error.code: SETTINGS_CRYPTO_UNAVAILABLE (was INTERNAL_ERROR)
  • error.details: { namespace, key } — the located refusal (never the value)
  • error.message: unchanged; carries the operator prescription (wire SettingsServicePluginOptions.cryptoProvider or a real crypto adapter)

SettingsView.tsx already branches per code (SETTINGS_LOCKED is special-cased around line 152; SETTINGS_VALIDATION feeds the field-error extractor). SETTINGS_CRYPTO_UNAVAILABLE currently falls to the generic error path, which does surface the carried message — so nothing is broken; this is the client-side half the new code exists to enable: an admin saving a secret on a deployment that cannot encrypt should see "this deployment cannot encrypt secrets — reconfigure it" as a first-class state (pointing at the refused key via error.details.key), not a generic save failure.

Severity: low, enhancement-class. The framework half is done; nothing here blocks it.


Generated by Claude Code

Activity

  1. self-assigned this
    on Aug 13, 2026
  2. yinlianghui commented on Aug 13, 2026

    @yinlianghui
    Contributor

    CLAIM — session_017Qqyix2QcnpUC9XeYVDzx3, branch claude/issue-4570-crypto-unavailable-refusal. Dispatching a dev agent now.

    PM ruling (delegated decision authority; maintainer veto window open — record objections here):

    1. Mirror the SETTINGS_LOCKED precedent (SettingsView.tsx ~:152 — measure its exact shape first: how it detects the code, what state it renders, how it recovers). Add the SETTINGS_CRYPTO_UNAVAILABLE branch as a first-class refusal state: name the refused key from error.details.key (and namespace), carry the message's operator prescription verbatim (the server owns that copy — do not restate it client-side), and distinguish it visually the way LOCKED does from a generic failure.
    2. The wire envelope is the fixture: red-first with the fix(plugin-detail): DetailSection shares ONE definition of emptiness, and it trims #8396 shape (status 500, code SETTINGS_CRYPTO_UNAVAILABLE, details {namespace, key}) — today it renders the generic path (pin that as the pre-fix observation), post-fix the dedicated state. Pins: SETTINGS_LOCKED and SETTINGS_VALIDATION branches byte-untouched and green; an UNKNOWN code still takes the generic path (the fallback must not narrow); the value never appears anywhere (the envelope's own never-the-value rule).
    3. i18n through the surface's measured channel (defaults maps if that is what SettingsView uses; all-pack parity if locale packs are touched; the prescription itself stays the server's string).
    4. Changeset: '@object-ui/app-shell' patch expected (measure via .d.ts both ways). Never major.

    Mutual exclusion: SettingsView's surface is untouched by all in-flight cards (#4548 react+dashboard/report, #4566 core+DatasetWidget, #4563 apps/console entry, #4535 types+ObjectGrid exportOptions, #4567 landed). Disjoint throughout.


    Generated by Claude Code


    Generated by Claude Code

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

Metadata

Metadata

Assignees

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