Thanks to visit codestin.com
Credit goes to github.com

Skip to content

fix(coderd): require bedrock models when seeding providers from env - #27868

Closed
evgeniy-scherbina wants to merge 1 commit into
mainfrom
yevhenii/bedrock-validation
Closed

fix(coderd): require bedrock models when seeding providers from env#27868
evgeniy-scherbina wants to merge 1 commit into
mainfrom
yevhenii/bedrock-validation

Conversation

@evgeniy-scherbina

@evgeniy-scherbina evgeniy-scherbina commented Aug 4, 2026

Copy link
Copy Markdown
Contributor

Follow-up to: #27846
Related to: https://linear.app/codercom/issue/AIGOV-564/aibridge-bedrock-provider-skipped-404-on-all-routes-when-settings-omit

An env-seeded Bedrock provider always uses the InvokeModel protocol, which substitutes the configured models into every upstream request. When BEDROCK_MODEL or BEDROCK_SMALL_FAST_MODEL is missing the provider fails to build and is skipped, so every request routed to it returns a bare 404 with nothing explaining why.

Validate both model identifiers while seeding providers from the environment and fail startup with a descriptive error instead of writing a dead provider row. This applies to the legacy CODER_AI_GATEWAY_BEDROCK_* options, where the defaults mean it only fires if an operator sets one to the empty string, and to the indexed CODER_AI_GATEWAY_PROVIDER_<N>_* options, which carry no defaults.


For AGENTS: Review this in the context of #27846. Does it address the gaps introduced by that PR? Identify any remaining gaps or edge cases.

@evgeniy-scherbina
evgeniy-scherbina force-pushed the yevhenii/bedrock-validation branch from 538eadb to cb312e6 Compare August 4, 2026 20:32
@evgeniy-scherbina
evgeniy-scherbina marked this pull request as ready for review August 5, 2026 17:03
@linear-code

linear-code Bot commented Aug 5, 2026

Copy link
Copy Markdown

AIGOV-564

@evgeniy-scherbina

Copy link
Copy Markdown
Contributor Author

/coder-agents-review

@coder-agents-review

coder-agents-review Bot commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

Chat: Review posted | View chat
Requested: 2026-08-05 17:07 UTC by @evgeniy-scherbina

Review history
  • R1 (2026-08-05), 1 Note, 1 P1, 2 P2, 1 P3, COMMENT. Review

deep-review v0.9.0 | Round 1 | 1c993c7..cb312e6

Last posted: Round 1, 5 findings (1 P1, 2 P2, 1 P3, 1 Note), COMMENT. Review

Finding inventory

Finding inventory, PR #27868

Findings

# Sev Status Location Summary Round Reviewer Posted
CRF-1 P1 Open coderd/ai_providers_migrate.go:422 Already-seeded Bedrock row with empty models is never repaired; drift hash excludes models, so the remediation the error prints leaves the 404 in place R1 Netero Yes
CRF-2 P2 Open coderd/ai_providers_migrate.go:421 Base-URL-only indexed Bedrock provider still seeds a row the runtime refuses to build; seed predicate is wider than the runtime's R1 Netero Yes
CRF-3 P2 Open coderd/ai_providers_migrate.go:484 validateSeededBedrockModels duplicates codersdk.validateAIProviderBedrockModels and drops its protocol awareness R1 Netero Yes
CRF-4 P3 Open coderd/ai_providers_migrate.go:422 Indexed error names BEDROCK_MODEL, which is not a settable env var, and identifies the provider by name while the env vars are indexed R1 Netero Yes
CRF-5 Note Open coderd/ai_providers_migrate.go:346 Blast radius change: a misconfiguration that today costs one dead provider now blocks startup R1 Netero Yes

Contested and acknowledged

None.

Law analysis

Not run. Effective additions 92, below the 1000 threshold.

Round log

Round 1

Netero-only first pass (P1 present, panel gated). 1 P1, 2 P2, 1 P3, 1 Note. Reviewed against 1c993c7..cb312e6.
Orchestrator verification: confirmed canonicalAIProvider excludes Model/SmallFastModel (coderd/ai_providers_migrate.go:218-227) and that the found branch continues on hash equality without updating Settings (143-146), which is CRF-1's mechanism. Confirmed codersdk.IsBedrockConfigured returns true on base URL alone (codersdk/aiproviders_bedrock.go:126-128) while cli/aibridged.go:342-350 gates bedrockConfig on settings.IsConfigured() only, which is CRF-2's mechanism. Confirmed codersdk/aiproviders.go:396-421 already encodes the requirement and is protocol-aware, which is CRF-3. Confirmed the indexed env key suffix at cli/server.go:3383-3386 is reached only under the CODER_AI_GATEWAY_PROVIDER__ / CODER_AIBRIDGE_PROVIDER__ prefixes (3170-3196), which is CRF-4.

About deep-review

CRF = Coder Review Finding (P0-P4, Nit, Note)

Reviewer Focus
Bisky tests
Chopper ops/errors
Churn-guard change verification
Ging language modernization
Gon naming
Hisoka edge cases
Killua perf
Kite change integrity
Knov contracts
Knuckle SQL
Komugi flake/determinism
Kurapika security
Law decomposition
Leorio docs
Luffy product
Mafu-san process
Mafuuu contracts
Melody dispatch/pairing
Meruem structural
Nami frontend
Netero mechanical checks
Pariston premise testing
Pen-botter product gaps
Razor verification
Robin duplication
Ryosuke Go arch
Takumi concurrency
Zoro shape

🤖 Managed by Coder Agents.

@coder-agents-review coder-agents-review Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

First-pass review only. These are mechanical findings from a single first-pass reviewer; the full review panel has not reviewed this PR yet and will do so after these are addressed.

The validation itself is well placed: it runs before any database work, both call sites are covered by real tests (reverting the production change makes both new subtests fail, so they are not tautological), and the error strings carry provider identity plus remediation. Test density is 64%, and the added SmallFastModel fields in the five pre-existing subtests are needed to keep those tests on their original paths rather than padding.

The PR body asks whether this closes the gaps from #27846 and what remains. It closes one gap in a class of gaps, and the two most significant findings are about what it leaves open: deployments that already hit AIGOV-564 are not repaired by this change (CRF-1), and a base-URL-only indexed Bedrock provider still produces the same dead row and bare 404 (CRF-2).

Findings: 1 P1, 2 P2, 1 P3, 1 Note.

Netero on CRF-2: "Root cause is the class, not the instance: the seed's Bedrock predicate is wider than the set of rows the runtime can build, and this PR closes one gap in that gap set (missing models) while leaving the other."

🤖 This review was automatically generated with Coder Agents.

// indexed ones carry no defaults, so a provider migrated from
// legacy to indexed env vars loses its models silently.
if err := validateSeededBedrockModels(bedrock); err != nil {
return nil, xerrors.Errorf("indexed AI provider %q: %w, set BEDROCK_MODEL and BEDROCK_SMALL_FAST_MODEL on it", name, err)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 [CRF-1] The remediation this error prints does not repair a Bedrock row that was already seeded without models, so startup goes green while the provider keeps returning 404. (Netero)

The drift hash deliberately excludes Model and SmallFastModel (canonicalAIProvider, lines 220-227). In the case found: branch the seed compares hashes and continues on equality (lines 143-145); it never updates Settings.

  1. Restart after this PR: startup fails with "set BEDROCK_MODEL and BEDROCK_SMALL_FAST_MODEL on it".
  2. Operator sets both env vars.
  3. Startup succeeds, the stored row is untouched with empty models, buildProvider still refuses it (cli/aibridged.go, bedrock branch), and every request to the provider still 404s.

Verified: I inserted a Bedrock row with {"region":"us-east-1"} settings, ran SeedAIProvidersFromEnv with both models set in the indexed config, and asserted the stored settings afterwards. SeedAIProvidersFromEnv returned nil and stored.Bedrock.Model / stored.Bedrock.SmallFastModel were both still empty.

I verified both halves of the mechanism independently: canonicalAIProvider hashes only type, base URL, region, and keys hash, and the found branch returns early on hash equality without touching Settings.

This is the population the linked issue describes: indexed env vars have no model defaults, so any existing indexed Bedrock provider configured without BEDROCK_MODEL already has the broken row. For them the new error is a detour, not a fix, and the message actively misleads by naming env vars that cannot repair the row. Either include the models in the canonical hash so the stale row surfaces as drift, or detect the models-empty existing row and say the row must be fixed through the API.

🤖

// Unlike the legacy CODER_AI_GATEWAY_BEDROCK_* options, the
// indexed ones carry no defaults, so a provider migrated from
// legacy to indexed env vars loses its models silently.
if err := validateSeededBedrockModels(bedrock); err != nil {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 [CRF-2] The same dead-row-and-404 failure still ships for an indexed Bedrock provider configured with BEDROCK_BASE_URL only; models are validated, credentials-or-region are not. (Netero)

IsBedrockConfigured(baseURL, b) is true on base URL alone, so CODER_AI_GATEWAY_PROVIDER_0_TYPE=bedrock plus BEDROCK_BASE_URL, BEDROCK_MODEL, BEDROCK_SMALL_FAST_MODEL (region and credentials from the AWS environment, the documented VPC/FIPS case in IsBedrockConfigured's own doc comment) passes ReadAIProvidersFromEnv, passes the new model check, and is inserted. At runtime bedrockConfig gates on AIProviderBedrockSettings.IsConfigured(), which ignores Model/SmallFastModel, returns nil, and buildProvider refuses the spec.

Verified with a throwaway internal test in cli: buildProvider on that exact spec returns bedrock provider has no bedrock credentials configured while codersdk.IsBedrockConfigured reports true for the same input.

Confirmed by reading both predicates: IsBedrockConfigured returns baseURL != "" || b.IsConfigured(), while bedrockConfig consults only IsConfigured(), whose doc comment lists region, role ARN, and access keys. The seed and the runtime disagree on what a buildable Bedrock provider is, and this PR narrows that disagreement by one field instead of closing it. Gating the seed on what the runtime actually requires (bedrockConfig(baseURL, settings) != nil plus config.AWSBedrock.Validate()) closes both this and the missing-model case with one check.

🤖

// request. Without them the provider fails to build and is skipped,
// leaving every request to it to return a bare 404, so failing here
// keeps the dead row out of the database.
func validateSeededBedrockModels(b codersdk.AIProviderBedrockSettings) error {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 [CRF-3] validateSeededBedrockModels duplicates codersdk.validateAIProviderBedrockModels, added one commit earlier by the PR this follows up. (Netero)

codersdk/aiproviders.go:402 already encodes "invoke-model requires Model and SmallFastModel", is called from both CreateAIProviderRequest.Validate and UpdateAIProviderRequest.Validate, and carries the same rationale in its doc comment ("would be skipped at gateway startup and every request to it would 404"). git log -S confirms it landed in 1142706 (#27846), the immediate predecessor commit.

The copy is protocol-unaware: it requires both models unconditionally, where the original skips the check for non-invoke-model protocols.

Verified: the codersdk validator returns early unless ResolvedProtocol() == invoke-model, and the new copy has no protocol branch. That is harmless today only because the indexed env key switch has no BEDROCK_PROTOCOL case, and nothing in either file records that dependency. The day a protocol env key is added, seeding rejects valid mantle providers. Export the codersdk validator (or a thin wrapper over it) and call it from both paths so the requirement and its protocol condition live in one place.

🤖

// indexed ones carry no defaults, so a provider migrated from
// legacy to indexed env vars loses its models silently.
if err := validateSeededBedrockModels(bedrock); err != nil {
return nil, xerrors.Errorf("indexed AI provider %q: %w, set BEDROCK_MODEL and BEDROCK_SMALL_FAST_MODEL on it", name, err)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P3 [CRF-4] The indexed error names BEDROCK_MODEL, which is not an environment variable an operator can set. (Netero)

The real variables are CODER_AI_GATEWAY_PROVIDER_<N>_BEDROCK_MODEL and CODER_AI_GATEWAY_PROVIDER_<N>_BEDROCK_SMALL_FAST_MODEL (cli/server.go:3170, key switch at 3383). "on it" refers to a provider identified by name, but the env vars are indexed by number, so the operator has to map name back to index themselves. The sibling validator in ReadAIProvidersFromEnv already solves both problems: it reports provider %d (%s): ... with the index and it knows the active prefix (aiGatewayProviderEnvPrefix vs aiBridgeProviderEnvPrefix).

Confirmed: BEDROCK_MODEL is a key suffix reached only under the CODER_AI_GATEWAY_PROVIDER_<N>_ or CODER_AIBRIDGE_PROVIDER_<N>_ prefix, and the prefix in use is deployment-dependent. An operator following this message literally sets a variable that does nothing. Naming the index and the active prefix, or moving the check next to the existing BEDROCK_* consistency checks in ReadAIProvidersFromEnv, gives the correct name for free.

🤖

// configured models into every upstream request. Both options
// carry defaults, so this only fires when an operator sets one
// to the empty string.
if err := validateSeededBedrockModels(bedrock); err != nil {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note [CRF-5] The check turns a partially-broken deployment into a deployment that will not boot. (Netero)

SeedAIProvidersFromEnv failure is fatal in both entry points (cli/server.go:1168, enterprise/cli/server.go:186). An operator running today with an indexed Bedrock provider and no BEDROCK_MODEL has one dead provider and a working coderd; after this change coderd refuses to start. That matches the existing fail-fast behavior of this file (drift already aborts startup), so I am not filing it as a finding, but the blast radius is worth stating in the PR body.

Agreed on both counts: consistent with the file's existing behavior, and worth stating in the PR body so whoever rolls this out knows the failure mode changed from one degraded provider to a refused startup. Note only, no change requested.

🤖

@evgeniy-scherbina

Copy link
Copy Markdown
Contributor Author

We decided to remove support for the LegacyEnv and IndexedEnv provider configurations, so closing this PR.

@github-actions github-actions Bot locked and limited conversation to collaborators Aug 6, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant