Skip to content

fix(router): include Bedrock batch/S3 fields and model in deployment credentials - #24548

Merged
mateo-berri merged 1 commit into
BerriAI:litellm_internal_stagingfrom
mpcusack-altos:fix/bedrock-batch-credential-fields
Aug 6, 2026
Merged

fix(router): include Bedrock batch/S3 fields and model in deployment credentials#24548
mateo-berri merged 1 commit into
BerriAI:litellm_internal_stagingfrom
mpcusack-altos:fix/bedrock-batch-credential-fields

Conversation

@mpcusack-altos

@mpcusack-altos mpcusack-altos commented Mar 25, 2026

Copy link
Copy Markdown
Contributor

TLDR

Problem this solves:

  • Bedrock batch creation through the proxy failed even with correct config
  • Credential resolution silently dropped the batch/S3 fields and the model

How it solves it:

  • Declares s3_region_name, s3_encryption_key_id, aws_batch_role_arn on CredentialLiteLLMParams
  • Returns the deployment's model from get_deployment_credentials_with_provider
  • Provider-only calls still get no model kwarg injected

User Flow

Before: the flow dies at batch creation, with a different error depending on whether the model is repeated in the create body

  1. POST https://litellm-domain/v1/files (multipart: the 100-request JSONL, purpose=batch, model=bedrock-haiku-batch) returns 200 with a long scrambled file id and status "uploaded"
  2. POST https://litellm-domain/v1/batches with {"input_file_id": "", "endpoint": "/v1/chat/completions", "completion_window": "24h"} returns 400: "LiteLLM doesn't support custom_llm_provider=bedrock for 'create_batch'"
  3. Retrying with "model": "bedrock-haiku-batch" added to the body returns 500: "AWS IAM role ARN is required for Bedrock batch jobs. Set 'aws_batch_role_arn' in litellm_params or AWS_BATCH_ROLE_ARN env var", even though config.yaml sets exactly that field
  4. No batch job ever appears in the AWS account

After: the same upload plus the minimal create body produce a real Bedrock batch job whose results can be downloaded once it finishes

  1. POST https://litellm-domain/v1/files (multipart: the 100-request JSONL, purpose=batch, model=bedrock-haiku-batch) returns 200 with a long scrambled file id and status "uploaded"
  2. POST https://litellm-domain/v1/batches with {"input_file_id": "", "endpoint": "/v1/chat/completions", "completion_window": "24h"} returns 200 with a batch id and status "validating"
  3. GET https://litellm-domain/v1/batches/{batch_id} shows the job progressing and eventually "completed" with an output_file_id
  4. GET https://litellm-domain/v1/files/{output_file_id}/content downloads the 100 model replies

Relevant issues

Fixes #25104

Linear ticket

Pre-Submission checklist

Please complete all items before asking a LiteLLM maintainer to review your PR

  • I have added meaningful tests
  • My PR passes all CI/CD checks (e.g., lint, format, unit tests)
  • My PR's scope is as isolated as possible; it only solves 1 specific problem
  • I have received a Greptile Confidence Score of at least 4/5 before requesting a maintainer review (Greptile reviews automatically once the PR is opened; only comment @greptileai to re-request a review after pushing changes)

Delays in PR merge?

If you're seeing a delay in your PR being merged, ping the LiteLLM Team on Slack (#pr-review).

Screenshots / Proof of Fix

Live proxy, real Bedrock spend in us-west-2. Config pins a Claude Haiku 4.5 deployment with aws_region_name, s3_bucket_name, and aws_batch_role_arn in litellm_params. Before runs at commit 0659738, after runs at 3d275d9

Before, upload succeeds:

$ curl -s http://localhost:51672/v1/files -H "Authorization: Bearer sk-1234" \
    -F "file=@batch100.jsonl" -F "purpose=batch" -F "model=bedrock-haiku-batch"
{"id":"file-bGl0ZWxsbTpzMzovL2xpdGVsbG0tcHJveHkvbGl0ZWxsbS1iZWRyb2NrLWZpbGVzLW...","status":"uploaded",...}

Before, create fails without a body model (the file id already encodes it):

$ curl -s http://localhost:51672/v1/batches -H "Authorization: Bearer sk-1234" -H "Content-Type: application/json" \
    -d '{"input_file_id":"<file id>","endpoint":"/v1/chat/completions","completion_window":"24h"}'
{"error":{"message":"litellm.BadRequestError: LiteLLM doesn't support custom_llm_provider=bedrock for 'create_batch'","type":"internal_server_error","param":null,"code":"400"}}

Before, create still fails with the body model because the role ARN was dropped from credentials:

$ curl -s http://localhost:51672/v1/batches ... -d '{"input_file_id":"<file id>",...,"model":"bedrock-haiku-batch"}'
{"error":{"message":"AWS IAM role ARN is required for Bedrock batch jobs. Set 'aws_batch_role_arn' in litellm_params or AWS_BATCH_ROLE_ARN env var","type":"internal_server_error","param":"None","code":"500"}}

After, the same upload plus the minimal create body submit a real job:

$ curl -s http://localhost:51873/v1/batches -H "Authorization: Bearer sk-1234" -H "Content-Type: application/json" \
    -d '{"input_file_id":"<file id>","endpoint":"/v1/chat/completions","completion_window":"24h"}'
{"id":"batch_bGl0ZWxsbTphcm46YXdzOmJlZHJvY2s6dXMtd2VzdC0yOjg4ODYwMjIyMzQyODptb2RlbC1pbnZvY2F0aW9uLWpvYi85NXVzYjE0eHJrNGo7...","status":"validating",...}

AWS confirms the job exists with the configured role and bucket:

$ aws bedrock get-model-invocation-job --region us-west-2 --job-identifier arn:aws:bedrock:us-west-2:...:model-invocation-job/95usb14xrk4j \
    --query '{status:status,modelId:modelId,roleArn:roleArn,inputS3:inputDataConfig.s3InputDataConfig.s3Uri}'
{
    "status": "Submitted",
    "modelId": "arn:aws:bedrock:us-west-2:...:inference-profile/us.anthropic.claude-haiku-4-5-20251001-v1:0",
    "roleArn": "arn:aws:iam::...:role/service-role/AmazonBedrockExecutionRoleForAgents_BB9HNW6V4CV",
    "inputS3": "s3://litellm-proxy/litellm-bedrock-files-us.anthropic.claude-haiku-4-5-20251001-v1-0-....jsonl"
}

After, retrieve and results download once the job completes:

$ curl -s http://localhost:51873/v1/batches/<batch id> -H "Authorization: Bearer sk-1234"
{"id":"batch_...","status":"completed","output_file_id":"file-...",...}

$ curl -s http://localhost:51873/v1/files/<output file id>/content -H "Authorization: Bearer sk-1234" | head -1
{"modelInput":{"messages":[{"role":"user","content":[{"type":"text","text":"Reply with exactly: pong 94"}]}],"max_tokens":16,"anthropic_version":"bedrock-2023-05-31"},"modelOutput":{"model":"claude-haiku-4-5-20251001","id":"msg_bdrk_018xyfL6nQhtuiLmSEotGSD5","type":"message","role":"assistant","content":[{"type":"text","text":"pong 94"}],"stop_reason":"end_turn",...},"recordId":"req-94"}

All 100 records completed with the exact requested reply

The issue's third symptom (inference-profile ARNs stripped to a bare model name) no longer reproduces at head: a deployment whose model is bedrock/arn:aws:bedrock:...:inference-profile/us.anthropic.claude-haiku-4-5-20251001-v1:0 created job dteqa9v1zg5p and AWS shows modelId as the full ARN, since job names no longer embed the model and the modelId passes through verbatim

Type

🐛 Bug Fix

Changes

CredentialLiteLLMParams is the credential whitelist that get_deployment_credentials_with_provider() filters litellm_params through for the model-scoped files, batches, and vector-store endpoints; any field it doesn't declare is silently dropped. s3_bucket_name and gcs_bucket_name were promoted to it earlier, but s3_region_name, s3_encryption_key_id, and aws_batch_role_arn were still missing, so Bedrock batch creation failed asking for a role ARN the config already set. This PR declares the three fields there and removes the now redundant redeclarations on GenericLiteLLMParams

get_deployment_credentials_with_provider() also never returned the deployment's model, so the proxy's model-encoded-file-id path called litellm.acreate_batch() without one, skipped the provider-config dispatch that loads BedrockBatchesConfig, and 400'd with "LiteLLM doesn't support custom_llm_provider=bedrock for 'create_batch'". The credentials dict now carries model. Provider-only calls keep their existing no-model contract: get_team_provider_credentials() strips the key so a provider-scoped request is not silently pinned to whichever deployment happened to match, which the existing scenario tests continue to assert

Regression test: test_get_deployment_credentials_with_provider_bedrock_batch_fields asserts the resolved credentials for a Bedrock deployment include the model and all three batch/S3 fields

Final Attestation

  • The tests check the right things, including the edge cases, and regressions in the respective real-world customer use-cases are not possible after this PR

@vercel

vercel Bot commented Mar 25, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
litellm Ready Ready Preview, Comment Apr 3, 2026 7:17pm

Request Review

@codspeed-hq

codspeed-hq Bot commented Mar 25, 2026

Copy link
Copy Markdown
Contributor

Merging this PR will not alter performance

✅ 31 untouched benchmarks


Comparing mpcusack-altos:fix/bedrock-batch-credential-fields (3d275d9) with litellm_internal_staging (ba91768)

Open in CodSpeed

@mpcusack-altos

Copy link
Copy Markdown
Contributor Author

Lint failure is a false positive. Both files its complaining about are untouched in this PR

@greptile-apps

greptile-apps Bot commented Mar 25, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR fixes Bedrock batch job creation through the LiteLLM proxy by ensuring that s3_region_name, s3_encryption_key_id, and aws_batch_role_arn are declared on CredentialLiteLLMParams (the Pydantic whitelist model used to filter deployment credentials) and that the deployment's model is included in the returned credentials dict so the batch routing layer can dispatch to BedrockBatchesConfig.

  • Adds three missing AWS/S3 fields to CredentialLiteLLMParams and removes their now-redundant re-declarations from the subclass GenericLiteLLMParams, which inherits them through the existing class hierarchy.
  • Returns model from get_deployment_credentials_with_provider for deployment-scoped callers (batch/file), while get_team_provider_credentials strips it for provider-only lookups so a shared provider key is not inadvertently pinned to a single deployment.
  • Adds a pure in-memory regression test covering all three batch fields and the returned model value; updates the generated TypeScript schema accordingly.

Confidence Score: 5/5

  • This PR is safe to merge — the changes are narrowly scoped to credential field propagation and routing metadata, with a clear before/after test and live-traffic evidence in the PR description.
  • All three changes are independently correct: the whitelist model now declares the fields it was silently dropping, the model is injected unconditionally from a required deployment field, and the provider-only code path strips it to preserve existing semantics. The regression test and the TypeScript schema update are consistent with the Python changes. No existing test behavior is weakened.
  • No files require special attention.

Important Files Changed

Filename Overview
litellm/types/router.py Moves s3_region_name, s3_encryption_key_id, and aws_batch_role_arn from GenericLiteLLMParams into the parent CredentialLiteLLMParams whitelist, and removes redundant declarations from the subclass. The inheritance chain is preserved correctly; gcs_bucket_name and s3_bucket_name were already in the parent, so only the three new fields are net additions.
litellm/router.py Adds credentials["model"] = deployment.litellm_params.model after the credential whitelist filter, so batch/file callers receive the deployment model needed to route to the Bedrock batch config. The placement is correct: after credential resolution but before returning, ensuring the model is always present for deployment-scoped calls.
litellm/proxy/openai_files_endpoints/common_utils.py Strips the newly added model key from credentials returned by get_team_provider_credentials so provider-scoped callers are not silently pinned to a specific deployment model. The dict comprehension is the minimal correct change.
tests/test_litellm/test_router.py Adds a pure in-memory regression test covering all three new batch/S3 fields and the returned model value. No real network calls are made, consistent with the tests/test_litellm/ convention.
ui/litellm-dashboard/src/lib/http/schema.d.ts Auto-generated TypeScript schema updated to reflect the three new fields (aws_batch_role_arn, s3_region_name, s3_encryption_key_id) in the relevant param interfaces. Change appears correct and consistent with the Python model additions.

Reviews (7): Last reviewed commit: "fix(router): return model and Bedrock ba..." | Re-trigger Greptile

Comment thread litellm/types/router.py Outdated
Comment thread litellm/router.py
@@ -7373,6 +7373,9 @@ def get_deployment_credentials_with_provider(
# Remove the credential name since we've resolved it
credentials.pop("litellm_credential_name", None)

# Add model (needed for provider-config routing in batches/files)
credentials["model"] = deployment.litellm_params.model

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 model now present in credentials for all callers

After this change every call to prepare_data_with_credentials — including afile_retrieve, afile_delete, afile_content, and the vector-store file endpoints — will receive model merged into their data dict via data.update(credentials).

For acreate_batch/acreate_file this is intentional and correct. For file-retrieval / file-delete operations the model key flows into **data as an unknown kwarg, which is silently absorbed by those functions' GenericLiteLLMParams(**kwargs) with extra="allow". There is no regression today, but it is worth noting that the semantics of get_deployment_credentials_with_provider have expanded: it now returns routing metadata (model) alongside credentials. If future callers are added that pass these credentials to an API that does not accept an arbitrary model kwarg, they would need to strip it explicitly.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I think this is ok. Even if it's None. Thoughts?

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.

Yes, this is reasonable. A few considerations:

1. model=None would still break batch routing

If deployment.litellm_params.model is None, the batch endpoint will still hit the original error ("LiteLLM doesn't support custom_llm_provider=bedrock for 'create_batch'") because ProviderConfigManager.get_provider_batches_config() needs a model to route. So functionally, None here is no worse than the bug you're fixing—it just means the deployment is misconfigured.

2. Valid deployments should always have a model

For any functioning batch/file deployment, model should be set in litellm_params. A None here would indicate a malformed config, not a valid use case.

3. The semantic expansion is acceptable

Mixing routing metadata (model) into credentials is pragmatic. The alternative—restructuring get_deployment_credentials_with_provider to return (credentials, metadata)—is far more invasive and breaks all existing callers. The current approach:

  • Works for existing callers (they absorb it via extra="allow")
  • Solves the immediate bug
  • Has low risk (future callers will likely also use Pydantic models with extra="allow")

If you're concerned about None validation, you could add:

if deployment.litellm_params.model is None:
    raise ValueError("model is required in litellm_params for batch/file operations")

But given the scope of this fix and the fact that None would fail downstream anyway, shipping as-is seems fine.

@mpcusack-altos
mpcusack-altos marked this pull request as ready for review March 25, 2026 05:07
@mpcusack-altos

Copy link
Copy Markdown
Contributor Author

@greptileai

@codecov

codecov Bot commented Apr 3, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

@mpcusack-altos
mpcusack-altos force-pushed the fix/bedrock-batch-credential-fields branch from 47f02dd to 4df4ce9 Compare June 22, 2026 06:57
@mpcusack-altos
mpcusack-altos changed the base branch from main to litellm_internal_staging June 22, 2026 06:57
@mpcusack-altos
mpcusack-altos force-pushed the fix/bedrock-batch-credential-fields branch from 4df4ce9 to 8209278 Compare June 22, 2026 07:08
@mpcusack-altos
mpcusack-altos changed the base branch from litellm_internal_staging to litellm_oss_branch June 22, 2026 07:08
@mpcusack-altos

Copy link
Copy Markdown
Contributor Author

@greptile-apps

@mpcusack-altos
mpcusack-altos force-pushed the fix/bedrock-batch-credential-fields branch from 8209278 to df70e25 Compare June 25, 2026 09:38
@mpcusack-altos
mpcusack-altos changed the base branch from litellm_oss_branch to litellm_internal_staging June 25, 2026 09:39
@CLAassistant

CLAassistant commented Jun 25, 2026

Copy link
Copy Markdown

CLA assistant check
All committers have signed the CLA.

@mpcusack-altos
mpcusack-altos force-pushed the fix/bedrock-batch-credential-fields branch 3 times, most recently from 570775d to 6b32cb3 Compare June 25, 2026 10:51
@mpcusack-altos

Copy link
Copy Markdown
Contributor Author

Rebased onto litellm_internal_staging and retargeted the base, so CI is running clean now

One thing to call out for review: the budget-ratchet check is red, intentionally. The gating lint job passes; budget-ratchet is non-gating and turns red whenever a *-budget.json ceiling is raised, so the loosening is visible here

I raised reportUnknownArgumentType in basedpyright-code-budget.json by 231 (ceiling 33603 -> 33834, slack unchanged). Declaring s3_region_name and aws_batch_role_arn on CredentialLiteLLMParams adds one reportUnknownArgumentType per untyped LiteLLMParams(**kwargs) construction site across the SDK, roughly 115 of them, which is a deterministic +231; basedpyright reports one unknown-argument per declared __init__ param at those call sites. The rule's slack was already almost fully consumed by base drift (base sits at 33478 against the old 33603 ceiling), so the delta-vs-base gate trips on any field added to this widely-constructed type. I bumped the ceiling by exactly the measured delta rather than re-baselining the whole rule

I kept the explicit-field approach (declaring the fields on CredentialLiteLLMParams) rather than switching it to extra="allow", to stay consistent with the credential-whitelist pattern accepted in #30241. If you would prefer extra="allow" instead, that version needs no budget change and actually lowers the count; happy to switch if that is the call

…entials

get_deployment_credentials_with_provider dropped s3_region_name,
s3_encryption_key_id, and aws_batch_role_arn because
CredentialLiteLLMParams never declared them, and it never returned the
deployment's model, so proxy batch creation against Bedrock failed with
"LiteLLM doesn't support custom_llm_provider=bedrock for 'create_batch'"
or "AWS IAM role ARN is required" (BerriAI#25104)

Provider-only file and batch calls keep their no-model contract:
get_team_provider_credentials strips the model key so a provider-scoped
request is not pinned to an arbitrary matching deployment
@mateo-berri
mateo-berri force-pushed the fix/bedrock-batch-credential-fields branch from 6b32cb3 to 3d275d9 Compare August 6, 2026 04:49
@mateo-berri

Copy link
Copy Markdown
Contributor

Rebuilt this on current litellm_internal_staging: same credential fields plus returning the deployment model, verified live end to end on a real Bedrock batch job

@mateo-berri

Copy link
Copy Markdown
Contributor

@greptileai

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Bedrock batch inference broken via proxy: credential fields and model silently dropped

4 participants