diff --git a/.changeset/refused-read-no-retry.md b/.changeset/refused-read-no-retry.md
new file mode 100644
index 00000000000..71426419e96
--- /dev/null
+++ b/.changeset/refused-read-no-retry.md
@@ -0,0 +1,12 @@
+---
+'@object-ui/plugin-list': patch
+'@object-ui/plugin-detail': patch
+---
+
+fix(ui): a refused list or related-list read says "no access" and offers no Retry
+
+Clause-②: no
+Metadata-protocol: none
+
+- **List view.** A permission refusal (403) on a list read draws the no-access panel without the Retry button, the same way an object whose API is switched off already did: a retry re-reaches the same refusal. A network failure or a 5xx keeps Retry.
+- **Related list.** A related list the reader may not read draws a quiet, neutral line (`role="status"`, no destructive styling) with the list's no-access sentence, and no Retry. It no longer shows the generic action refusal ("You do not have permission to perform this action.") in red to someone who only opened the record. Other read failures are drawn as before.
diff --git a/ui/packages/plugin-detail/src/RelatedList.tsx b/ui/packages/plugin-detail/src/RelatedList.tsx
index 8504bb7aa4a..6f2f777146a 100644
--- a/ui/packages/plugin-detail/src/RelatedList.tsx
+++ b/ui/packages/plugin-detail/src/RelatedList.tsx
@@ -577,15 +577,22 @@ function serverSentence(err: unknown): string | null {
* the shared fixed sentence and never the server's text (a refused read does
* not name what it cannot see), unless the producer marked a `userMessage` for
* the reader; every other failure says the server's sentence when it gave one.
- * Retry for every kind but `api-disabled`, whose verdict a retry cannot change
- * — the ListView precedent.
+ * Retry for every kind but `api-disabled` and `forbidden`, whose verdicts a
+ * retry cannot change — the ListView precedent.
+ *
+ * A `forbidden` read is not a failure of anything the reader did: it is drawn
+ * as a quiet neutral line (`role="status"`, no destructive styling) and always
+ * says the list's no-access sentence. A producer's `userMessage` on a 403 is
+ * the generic action refusal ("...perform this action"), which reads as a
+ * rebuke to someone who only opened the record to read it.
*/
const RelatedListLoadFailed: React.FC<{ error: unknown; onRetry: () => void }> = ({ error, onRetry }) => {
const { t } = useDetailTranslation();
const kind = classifyLoadError(error);
const copy = LOAD_FAILED_COPY[kind];
+ const refused = kind === 'forbidden';
const message =
- declaredUserMessage(error)
+ (refused ? null : declaredUserMessage(error))
?? (copy.message
? t(copy.message[0], { defaultValue: copy.message[1] })
: serverSentence(error)
@@ -593,15 +600,18 @@ const RelatedListLoadFailed: React.FC<{ error: unknown; onRetry: () => void }> =
const Icon = kind === 'network' || kind === 'rejected' ? AlertTriangle : ShieldAlert;
return (