Skip to content

Parse multiple SDK user agents in builder check#3149

Open
matthewlouisbrockman wants to merge 2 commits into
mainfrom
template-builder-needs-to-be-able-to-parse-mutliple-agents-en-1378
Open

Parse multiple SDK user agents in builder check#3149
matthewlouisbrockman wants to merge 2 commits into
mainfrom
template-builder-needs-to-be-able-to-parse-mutliple-agents-en-1378

Conversation

@matthewlouisbrockman

@matthewlouisbrockman matthewlouisbrockman commented Jun 30, 2026

Copy link
Copy Markdown
Contributor

template builder checks the sdk version to decide whtehr to use the beta builder or ga builder. crashes on multiple user agents so this splits it up.

makes a loop to check each of the user agents for compatibility with template v2; the first agent should be the right one, but if it isn't ordered right for some reason will still check the rest

@linear-code

linear-code Bot commented Jun 30, 2026

Copy link
Copy Markdown

EN-1378

@cla-bot cla-bot Bot added the cla-signed label Jun 30, 2026
@cursor

cursor Bot commented Jun 30, 2026

Copy link
Copy Markdown

PR Summary

Low Risk
Narrow change to how template builder version is derived from User-Agent; no auth or data-path impact, with tests locking expected behavior.

Overview
Fixes template build start failing when the User-Agent header contains several space-separated tokens (for example SDK plus CLI). userAgentToTemplateVersion now walks each token and picks GA vs beta template v2 from the first e2b-js-sdk or e2b-python-sdk entry, instead of treating the whole header as one SDK string (which broke semver checks). Unrecognized or empty agents still default to the latest template version, with debug logging only after no SDK token is found. Table-driven tests cover single and composite agents, old SDK versions, and fallbacks.

Reviewed by Cursor Bugbot for commit 98f5ae1. Bugbot is set up for automated code reviews on this repo. Configure here.

@matthewlouisbrockman matthewlouisbrockman changed the title Parse multiple SDK user agents Parse multiple SDK user agents in builder check Jun 30, 2026

@gemini-code-assist gemini-code-assist 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.

Code Review

Failing the entire build request with an error when the User-Agent header contains an invalid semver version is fragile because User-Agent headers are client-controlled. Instead of returning an error when parsing JS or Python SDK versions fails, log a warning and fall back to a safe default version.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment on lines +218 to +220
if err != nil {
return "", fmt.Errorf("parsing JS SDK version: %w", 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.

high

Failing the entire build request with a 400 Bad Request error when the User-Agent header contains an invalid semver version is extremely fragile. User-Agent headers are client-controlled and can easily be malformed, customized, or modified by proxies. Instead of returning an error, log a warning and fall back to a safe default version.

			if err != nil {
				logger.Debug(ctx, "failed to parse JS SDK version from user agent", zap.Error(err), zap.String("user_agent", userAgent))
				return templates.TemplateV2BetaVersion, nil
			}

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.

existing behavior

Comment on lines +231 to +233
if err != nil {
return "", fmt.Errorf("parsing Python SDK version: %w", 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.

high

Failing the entire build request with a 400 Bad Request error when the User-Agent header contains an invalid semver version is extremely fragile. User-Agent headers are client-controlled and can easily be malformed, customized, or modified by proxies. Instead of returning an error, log a warning and fall back to a safe default version.

			if err != nil {
				logger.Debug(ctx, "failed to parse Python SDK version from user agent", zap.Error(err), zap.String("user_agent", userAgent))
				return templates.TemplateV2BetaVersion, nil
			}

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.

existing behavior

@codecov

codecov Bot commented Jun 30, 2026

Copy link
Copy Markdown

❌ 3 Tests Failed:

Tests completed Failed Passed Skipped
3104 3 3101 8
View the top 3 failed test(s) by shortest run time
github.com/e2b-dev/infra/tests/integration/internal/tests/api/templates::TestTemplateBuildRUN
Stack Traces | 0s run time
=== RUN   TestTemplateBuildRUN
=== PAUSE TestTemplateBuildRUN
=== CONT  TestTemplateBuildRUN
--- FAIL: TestTemplateBuildRUN (0.00s)
github.com/e2b-dev/infra/tests/integration/internal/tests/envd::TestListDir/depth_1_lists_root_directory
Stack Traces | 0.01s run time
=== RUN   TestListDir/depth_1_lists_root_directory
=== PAUSE TestListDir/depth_1_lists_root_directory
=== CONT  TestListDir/depth_1_lists_root_directory
    filesystem_test.go:96: 
        	Error Trace:	.../tests/envd/filesystem_test.go:96
        	Error:      	Received unexpected error:
        	            	unavailable: 502 Bad Gateway
        	Test:       	TestListDir/depth_1_lists_root_directory
--- FAIL: TestListDir/depth_1_lists_root_directory (0.01s)
github.com/e2b-dev/infra/tests/integration/internal/tests/envd::TestListDir/depth_2_lists_first_level_of_subdirectories_(in_this_case_the_root_directory)
Stack Traces | 0.01s run time
=== RUN   TestListDir/depth_2_lists_first_level_of_subdirectories_(in_this_case_the_root_directory)
=== PAUSE TestListDir/depth_2_lists_first_level_of_subdirectories_(in_this_case_the_root_directory)
=== CONT  TestListDir/depth_2_lists_first_level_of_subdirectories_(in_this_case_the_root_directory)
    filesystem_test.go:96: 
        	Error Trace:	.../tests/envd/filesystem_test.go:96
        	Error:      	Received unexpected error:
        	            	unavailable: 502 Bad Gateway
        	Test:       	TestListDir/depth_2_lists_first_level_of_subdirectories_(in_this_case_the_root_directory)
--- FAIL: TestListDir/depth_2_lists_first_level_of_subdirectories_(in_this_case_the_root_directory) (0.01s)
github.com/e2b-dev/infra/tests/integration/internal/tests/envd::TestListDir/depth_3_lists_all_directories_and_files
Stack Traces | 0.01s run time
=== RUN   TestListDir/depth_3_lists_all_directories_and_files
=== PAUSE TestListDir/depth_3_lists_all_directories_and_files
=== CONT  TestListDir/depth_3_lists_all_directories_and_files
    filesystem_test.go:96: 
        	Error Trace:	.../tests/envd/filesystem_test.go:96
        	Error:      	Received unexpected error:
        	            	unavailable: 502 Bad Gateway
        	Test:       	TestListDir/depth_3_lists_all_directories_and_files
--- FAIL: TestListDir/depth_3_lists_all_directories_and_files (0.01s)
github.com/e2b-dev/infra/tests/integration/internal/tests/envd::TestListDir/depth_0_lists_only_root_directory
Stack Traces | 0.03s run time
=== RUN   TestListDir/depth_0_lists_only_root_directory
=== PAUSE TestListDir/depth_0_lists_only_root_directory
=== CONT  TestListDir/depth_0_lists_only_root_directory
    filesystem_test.go:96: 
        	Error Trace:	.../tests/envd/filesystem_test.go:96
        	Error:      	Received unexpected error:
        	            	unavailable: 502 Bad Gateway
        	Test:       	TestListDir/depth_0_lists_only_root_directory
--- FAIL: TestListDir/depth_0_lists_only_root_directory (0.03s)
github.com/e2b-dev/infra/tests/integration/internal/tests/envd::TestListDir
Stack Traces | 1.01s run time
=== RUN   TestListDir
=== PAUSE TestListDir
=== CONT  TestListDir
--- FAIL: TestListDir (1.01s)
github.com/e2b-dev/infra/tests/integration/internal/tests/api/templates::TestTemplateBuildRUN/Single_RUN_command
Stack Traces | 164s run time
=== RUN   TestTemplateBuildRUN/Single_RUN_command
=== PAUSE TestTemplateBuildRUN/Single_RUN_command
=== CONT  TestTemplateBuildRUN/Single_RUN_command
    build_template_test.go:133: test-ubuntu-run: [info] Building template h3t2c64ebp7hc36scza7/8d916ce2-f8c7-4d4b-bcc2-7eea0e22ba54
    build_template_test.go:133: test-ubuntu-run: [info] [base] FROM ubuntu:22.04 [f9f564014e009a9561a82bf8c84f9314242971e833fb019936654ecba452f184]
    build_template_test.go:133: test-ubuntu-run: [info] Base Docker image size: 30 MB
    build_template_test.go:133: test-ubuntu-run: [info] Creating file system and pulling Docker image
    build_template_test.go:133: test-ubuntu-run: [info] Uncompressing layer sha256:40d16f30db405106ef8074779bdf41f012465c2a785bbeaa2eab9f2081099b47 30 MB
    build_template_test.go:133: test-ubuntu-run: [info] Uncompressing layer sha256:60872d57b32b28d1b29e29e9b3fb50ed901b2b41ab2cbb05b81f27118df1292b 13 MB
    build_template_test.go:133: test-ubuntu-run: [info] Uncompressing layer sha256:8c4b1b28875140ed3abacaf16ad0d696f6bef912f52d2148f261a23e3349465b 168 B
    build_template_test.go:133: test-ubuntu-run: [info] Layers extracted
    build_template_test.go:133: test-ubuntu-run: [info] Root filesystem structure: bin, boot, dev, etc, home, lib, lib32, lib64, libx32, media, mnt, opt, proc, root, run, sbin, srv, sys, tmp, usr, var
    build_template_test.go:133: test-ubuntu-run: [info] Provisioning sandbox template
    build_template_test.go:133: test-ubuntu-run: [info] Provisioning was successful, cleaning up
    build_template_test.go:133: test-ubuntu-run: [info] Sandbox template provisioned
    build_template_test.go:133: test-ubuntu-run: [info] [base] DEFAULT USER user [49e586c2171254c6bc4a09e84eedac32dbcf113a158c24248129af2f49cbed74]
    build_template_test.go:133: test-ubuntu-run: [info] [builder 1/1] RUN echo 'Hello, World!' [c72b4f813c2a16b0fc1a1c5da7b1365a304cbac516b22dc304a71f70aae48ac0]
    build_template_test.go:133: test-ubuntu-run: [info] [builder 1/1] [stdout]: Hello, World!
    build_template_test.go:133: test-ubuntu-run: [info] [finalize] Finalizing template build [92c524e30533398ebb41ce04c2596130f0cdecc9aa328e28fdb16a1b11f61d62]
    build_template_test.go:133: test-ubuntu-run: [error] Build failed: build was cancelled
    build_template_test.go:166: Build failed: {<nil> build was cancelled <nil>}
--- FAIL: TestTemplateBuildRUN/Single_RUN_command (164.45s)
github.com/e2b-dev/infra/tests/integration/internal/tests/api/templates::TestTemplateBuildFromTemplateStartCommand
Stack Traces | 169s run time
=== RUN   TestTemplateBuildFromTemplateStartCommand
=== PAUSE TestTemplateBuildFromTemplateStartCommand
=== CONT  TestTemplateBuildFromTemplateStartCommand
    build_template_test.go:133: test-ubuntu-start-base: [info] Building template jfvmecxyrm57f076zhmh/b9b38cf3-cfe2-4afa-90a5-a61cb418fe6a
    build_template_test.go:133: test-ubuntu-start-base: [info] [base] FROM ubuntu:22.04 [f9f564014e009a9561a82bf8c84f9314242971e833fb019936654ecba452f184]
    build_template_test.go:133: test-ubuntu-start-base: [info] Base Docker image size: 30 MB
    build_template_test.go:133: test-ubuntu-start-base: [info] Creating file system and pulling Docker image
    build_template_test.go:133: test-ubuntu-start-base: [info] Uncompressing layer sha256:40d16f30db405106ef8074779bdf41f012465c2a785bbeaa2eab9f2081099b47 30 MB
    build_template_test.go:133: test-ubuntu-start-base: [info] Uncompressing layer sha256:60872d57b32b28d1b29e29e9b3fb50ed901b2b41ab2cbb05b81f27118df1292b 13 MB
    build_template_test.go:133: test-ubuntu-start-base: [info] Uncompressing layer sha256:8c4b1b28875140ed3abacaf16ad0d696f6bef912f52d2148f261a23e3349465b 168 B
    build_template_test.go:133: test-ubuntu-start-base: [info] Layers extracted
    build_template_test.go:133: test-ubuntu-start-base: [info] Root filesystem structure: bin, boot, dev, etc, home, lib, lib32, lib64, libx32, media, mnt, opt, proc, root, run, sbin, srv, sys, tmp, usr, var
    build_template_test.go:133: test-ubuntu-start-base: [info] Provisioning sandbox template
    build_template_test.go:133: test-ubuntu-start-base: [info] Provisioning was successful, cleaning up
    build_template_test.go:133: test-ubuntu-start-base: [info] Sandbox template provisioned
    build_template_test.go:133: test-ubuntu-start-base: [info] [base] DEFAULT USER user [49e586c2171254c6bc4a09e84eedac32dbcf113a158c24248129af2f49cbed74]
    build_template_test.go:133: test-ubuntu-start-base: [info] [builder 1/2] ENV START_ENV start_value [22ecb2c2ce1b1d262e37d2cd71b90e6d33fdf99ecfca361e1a2c284afb7fc08a]
    build_template_test.go:133: test-ubuntu-start-base: [info] [builder 2/2] WORKDIR /start/workdir [f841574929b92d8b6e15a8fc6477f0f1e568eac3d30ae7110a831bbcea62e921]
    build_template_test.go:133: test-ubuntu-start-base: [info] [finalize] Finalizing template build [a509f6bf83c53e4844e4d62e9e93f7dae0d5706e070f3f756fe6d227a04976e4]
    build_template_test.go:133: test-ubuntu-start-base: [error] Build failed: build was cancelled
    build_template_test.go:700: Build failed: {<nil> build was cancelled <nil>}
--- FAIL: TestTemplateBuildFromTemplateStartCommand (168.91s)

To view more test analytics, go to the Test Analytics Dashboard
📋 Got 3 mins? Take this short survey to help us improve Test Analytics.

@claude claude Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

LGTM - small, well-tested refactor to handle compound user-agent strings.

Extended reasoning...

Overview

Refactors userAgentToTemplateVersion in packages/api/internal/handlers/template_start_build_v2.go to tokenize the user agent on whitespace and check each token against the JS/Python SDK prefixes, rather than only checking the start of the full string. Adds a new test file covering current SDK, mixed SDK+CLI, old SDK, unrecognized, and empty user-agent cases.

Security risks

None. The function is a read-only lookup that maps a user-agent string to a template version constant. strings.Fields is safe; no injection or auth surface is touched.

Level of scrutiny

Low. This is a small, localized parsing fix in a single helper function with clear intent and comprehensive table-driven tests. The early-return behavior preserves the first-match-wins semantics that the description calls out.

Other factors

The new test cases include a v-prefixed version (e2b-js-sdk/v2.31.0) which IsGTEVersion handles via sanitizeVersion. The fallback debug log now correctly fires when no token matched (previously a default switch arm). No reviewer comments are outstanding.

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant