Skip to content

fix: return an error when docker host cannot be retrieved#3613

Merged
mdelapenya merged 1 commit intotestcontainers:mainfrom
ash2k:no-panic
Apr 9, 2026
Merged

fix: return an error when docker host cannot be retrieved#3613
mdelapenya merged 1 commit intotestcontainers:mainfrom
ash2k:no-panic

Conversation

@ash2k
Copy link
Copy Markdown
Contributor

@ash2k ash2k commented Mar 27, 2026

What does this PR do?

When I run some of my tests without Docker running, they panic. I'd like them to just return an error from testcontainers.Run() (and everything that is built on top).

Why is it important?

It's not useful to get a panic when just a normal error is enough. Panic should be reserved for programming errors/etc - "Docker is not running" is not that.

Related issues

@ash2k ash2k requested a review from a team as a code owner March 27, 2026 01:51
@netlify
Copy link
Copy Markdown

netlify Bot commented Mar 27, 2026

Deploy Preview for testcontainers-go ready!

Name Link
🔨 Latest commit 5fdd80b
🔍 Latest deploy log https://app.netlify.com/projects/testcontainers-go/deploys/69d72117baa01a0008a83250
😎 Deploy Preview https://deploy-preview-3613--testcontainers-go.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Mar 27, 2026

Summary by CodeRabbit

  • Bug Fixes
    • Docker host resolution no longer causes application panics when missing or invalid; failures are now reported as errors.
    • Client and provider initialization now propagate host-extraction errors instead of aborting, enabling graceful startup failure handling.
    • Logging updated to include the resolved Docker host when available.

Walkthrough

Docker host extraction was changed from panic-on-failure to explicit error-returning behavior: a new ExtractDockerHost(ctx) (string, error) was added with caching, and callsites now propagate errors instead of calling MustExtractDockerHost directly.

Changes

Cohort / File(s) Summary
Docker Host Extraction Logic
internal/core/docker_host.go
Add ExtractDockerHost(ctx) (string, error) with cached (host, err); refactor MustExtractDockerHost to call ExtractDockerHost and panic only if it returns an error.
Client & Provider Call Sites
internal/core/client.go, provider.go, docker_client.go
Replace MustExtractDockerHost(ctx) with ExtractDockerHost(ctx); add explicit error handling and early returns where extraction can fail; propagate resolved host into client/provider initialization and logs.

Sequence Diagram(s)

sequenceDiagram
    participant Caller
    participant Core as core.ExtractDockerHost
    participant Client as NewClient / NewDockerProvider / DockerClient

    Caller->>Client: initialize (ctx)
    Client->>Core: ExtractDockerHost(ctx)
    Core-->>Client: (host, nil)
    alt host present
        Client->>Client: use host in options/logs
        Client-->>Caller: return initialized client
    else error
        Core-->>Client: ("" , error)
        Client-->>Caller: return nil, error
    end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Poem

🐰 I hopped through code, no panic this time,
Extracted the host with a rhythm and rhyme,
Cached both the answer and why it could fail,
Callers get errors, no sudden derail.
A calm little change — hop, logged, and on trail. 🥕

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 75.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title 'fix: return an error when docker host cannot be retrieved' directly and clearly describes the main change in the changeset: replacing panic behavior with error returns when Docker host extraction fails.
Description check ✅ Passed The description explains the motivation (tests panicking without Docker), the desired behavior (return errors instead), and the rationale (panics reserved for programming errors). It is clearly related to the changeset.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@docker_client.go`:
- Around line 80-83: The cache flag dockerInfoSet is being set before host
resolution, so if core.ExtractDockerHost(ctx) fails the function returns an
error while leaving dockerInfoSet true and causing future Info() calls to return
a cached success; update Info() so dockerInfoSet is only set after
ExtractDockerHost succeeds (and any subsequent logging/processing completes) —
e.g., move the assignment that flips dockerInfoSet (or the code that writes
dockerInfo) to after the successful call to core.ExtractDockerHost(ctx) and any
error checks, or explicitly clear/unset dockerInfoSet on error paths of
ExtractDockerHost to ensure the cache is only marked initialized on success.

In `@internal/core/docker_host.go`:
- Around line 96-101: The code uses dockerHostOnce to cache both success and
failure in ExtractDockerHost, causing dockerHostErrCache to be sticky; change
the logic so dockerHostOnce (and dockerHostCache) only records on successful
extraction: call extractDockerHost inside a retryable path and only assign to
dockerHostCache and mark initialization (or call dockerHostOnce.Do) when
extractDockerHost returns nil error; if extractDockerHost returns an error,
return it directly without setting dockerHostErrCache so future calls can retry.
Update the related variables dockerHostOnce, dockerHostCache, dockerHostErrCache
and the call site of extractDockerHost to follow this pattern.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 64d4242b-1b52-498c-9c55-6f8f05a93aa9

📥 Commits

Reviewing files that changed from the base of the PR and between b7f3ae3 and 352b03e.

📒 Files selected for processing (4)
  • docker_client.go
  • internal/core/client.go
  • internal/core/docker_host.go
  • provider.go

Comment thread docker_client.go
Comment on lines +96 to 101
func ExtractDockerHost(ctx context.Context) (string, error) {
dockerHostOnce.Do(func() {
dockerHostCache, dockerHostErrCache = extractDockerHost(ctx)
})

return dockerHostCache
return dockerHostCache, dockerHostErrCache
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Do not cache Docker host extraction errors process-wide.

At Line 97, sync.Once stores the first failure forever (dockerHostErrCache). A transient startup failure becomes unrecoverable for the lifetime of the process, even if Docker/environment is fixed later.

Suggested fix (cache success only, allow retry on errors)
 var (
-	dockerHostCache    string
-	dockerHostErrCache error
-	dockerHostOnce     sync.Once
+	dockerHostCache  string
+	dockerHostCached bool
+	dockerHostMu     sync.Mutex
 )
@@
 func ExtractDockerHost(ctx context.Context) (string, error) {
-	dockerHostOnce.Do(func() {
-		dockerHostCache, dockerHostErrCache = extractDockerHost(ctx)
-	})
-	return dockerHostCache, dockerHostErrCache
+	dockerHostMu.Lock()
+	defer dockerHostMu.Unlock()
+
+	if dockerHostCached {
+		return dockerHostCache, nil
+	}
+
+	host, err := extractDockerHost(ctx)
+	if err != nil {
+		return "", err
+	}
+
+	dockerHostCache = host
+	dockerHostCached = true
+	return dockerHostCache, nil
 }

Also applies to: 34-36

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@internal/core/docker_host.go` around lines 96 - 101, The code uses
dockerHostOnce to cache both success and failure in ExtractDockerHost, causing
dockerHostErrCache to be sticky; change the logic so dockerHostOnce (and
dockerHostCache) only records on successful extraction: call extractDockerHost
inside a retryable path and only assign to dockerHostCache and mark
initialization (or call dockerHostOnce.Do) when extractDockerHost returns nil
error; if extractDockerHost returns an error, return it directly without setting
dockerHostErrCache so future calls can retry. Update the related variables
dockerHostOnce, dockerHostCache, dockerHostErrCache and the call site of
extractDockerHost to follow this pattern.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

♻️ Duplicate comments (1)
docker_client.go (1)

76-86: ⚠️ Potential issue | 🟠 Major

Defer cache finalization until host resolution succeeds.

dockerInfoSet is set at Line 52 before the new failure path at Lines 76-79. If host extraction fails once, later Info() calls can incorrectly return cached success from Line 44 and mask the error this PR is trying to propagate.

Suggested fix
@@
-	dockerInfo = res
-	dockerInfoSet = true
+	dockerInfo = res
@@
 	host, err := core.ExtractDockerHost(ctx)
 	if err != nil {
-		return dockerInfo, err
+		return res, err
 	}
@@
+	dockerInfoSet = true
 	return dockerInfo, nil
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docker_client.go` around lines 76 - 86, The code sets dockerInfoSet before
resolving the host, which can cache a successful state even when
core.ExtractDockerHost(ctx) fails; update the flow so that dockerInfoSet is only
set after host resolution succeeds: move or defer the assignment/update of
dockerInfoSet until after core.ExtractDockerHost(ctx) returns without error (the
success path used before logging), and ensure the Info() path checks for the
absence of dockerInfoSet when host extraction fails so it does not return a
cached success; touch the dockerInfoSet assignment and any early-return in
Info()/Docker client initialization and the call to core.ExtractDockerHost to
implement this change.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In `@docker_client.go`:
- Around line 76-86: The code sets dockerInfoSet before resolving the host,
which can cache a successful state even when core.ExtractDockerHost(ctx) fails;
update the flow so that dockerInfoSet is only set after host resolution
succeeds: move or defer the assignment/update of dockerInfoSet until after
core.ExtractDockerHost(ctx) returns without error (the success path used before
logging), and ensure the Info() path checks for the absence of dockerInfoSet
when host extraction fails so it does not return a cached success; touch the
dockerInfoSet assignment and any early-return in Info()/Docker client
initialization and the call to core.ExtractDockerHost to implement this change.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: fdec2d4a-9aaf-443d-823c-0d951e5a18d8

📥 Commits

Reviewing files that changed from the base of the PR and between 352b03e and 5fdd80b.

📒 Files selected for processing (4)
  • docker_client.go
  • internal/core/client.go
  • internal/core/docker_host.go
  • provider.go
✅ Files skipped from review due to trivial changes (1)
  • internal/core/docker_host.go
🚧 Files skipped from review as they are similar to previous changes (2)
  • internal/core/client.go
  • provider.go

@mdelapenya mdelapenya self-assigned this Apr 9, 2026
@mdelapenya mdelapenya added the bug An issue with the library label Apr 9, 2026
Copy link
Copy Markdown
Member

@mdelapenya mdelapenya left a comment

Choose a reason for hiding this comment

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

LGTM, thanks!

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

Labels

bug An issue with the library

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants