From cb9e3a46775a33646d96974bd2258a9f2b366ca9 Mon Sep 17 00:00:00 2001 From: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Fri, 16 Oct 2026 18:21:01 +0000 Subject: [PATCH] fix(skills): Correct the HTTP 403 advice now that server-side FDv2 is available to every account Co-Authored-By: traci@launchdarkly.com --- packages/client/README.md | 2 +- packages/client/src/__tests__/skills-fdv2.test.ts | 4 ++-- packages/client/src/skills-fdv2.ts | 5 +++-- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/packages/client/README.md b/packages/client/README.md index 8a790c27..f769aae2 100644 --- a/packages/client/README.md +++ b/packages/client/README.md @@ -446,7 +446,7 @@ try { **The connection carries only skills.** Every request declares the skill payload, so flag and segment objects do not arrive on it. Any other object kind is skipped, not rejected, and counted in `diagnostics.objectsIgnored`; a nonzero count means the payload has a kind this version does not recognise, not that something failed. -> **Beta caveats, worth knowing before you deploy.** Payload signing does not exist on this channel yet, so delivery is TLS-only and the content hash establishes self-consistency, not origin authenticity. The FDv2 protocol is opt-in per account: without it the endpoints return HTTP 403, which the store reports as a fatal error explaining what to do. `ld-relay` does not speak the FDv2 endpoints, so relay-only deployments cannot receive skills. +> **Beta caveats, worth knowing before you deploy.** Payload signing does not exist on this channel yet, so delivery is TLS-only and the content hash establishes self-consistency, not origin authenticity. The SDK-facing FDv2 endpoints are available to every account. An account without Agent Skills access receives no skill content rather than an error status. An HTTP 403 is reported as a fatal error explaining what to check. `ld-relay` does not speak the FDv2 endpoints, so relay-only deployments cannot receive skills. **If every skill comes back empty, check `diagnostics.hashlessObjects`.** Objects without a `contentHash` are withheld, so a nonzero count means skills are being withheld, not that the environment has none. The count is cumulative, not the current size of the withheld set. The store logs an error per hashless object, plus a summary when its contents become wholly hashless or the withheld set changes (not repeated for an unchanged re-delivery). There is no fallback that skips verification. diff --git a/packages/client/src/__tests__/skills-fdv2.test.ts b/packages/client/src/__tests__/skills-fdv2.test.ts index 8735468f..ff117658 100644 --- a/packages/client/src/__tests__/skills-fdv2.test.ts +++ b/packages/client/src/__tests__/skills-fdv2.test.ts @@ -2304,8 +2304,8 @@ describe('failure handling', () => { store.start(); expect(await waitUntil(() => store.failed !== null)).toBe(true); expect(store.failed).toContain('403'); - expect(store.failed).toContain('opt-in'); - expect(consoleErrors()).toContain('opt-in'); + expect(store.failed).toContain('proxy'); + expect(consoleErrors()).toContain('proxy'); }); it('stops on 401', async () => { diff --git a/packages/client/src/skills-fdv2.ts b/packages/client/src/skills-fdv2.ts index fda52ba8..286c295e 100644 --- a/packages/client/src/skills-fdv2.ts +++ b/packages/client/src/skills-fdv2.ts @@ -978,8 +978,9 @@ const REQUEST_ADVICE = 'the base URI and that the endpoint speaks FDv2.'; const FORBIDDEN_ADVICE = - 'The FDv2 protocol is opt-in per LaunchDarkly account and is served as HTTP 403 while it is off. Skill ' + - 'delivery needs it enabled; contact LaunchDarkly support to enable it for your account.'; + 'The SDK-facing FDv2 endpoints are available to every LaunchDarkly account, so check that the SDK key is the ' + + "environment's server-side SDK key and that no proxy in between is refusing the request. If neither explains " + + 'it, contact LaunchDarkly support.'; /** * `Retry-After` in milliseconds, or `null` when absent or unusable (blank, the