Skip to content

Commit fd3606b

Browse files
pinin4fjordsclaude
andcommitted
Skip format validation for cloud storage paths (S3, Azure, GCS)
Cloud storage paths (s3://, az://, gs://) cannot be reliably validated for accessibility or file/directory status without proper credentials configured. This causes validation failures with errors like: "could not validate file format of 's3://...': Unable to marshall request to JSON: Key cannot be empty" This fix skips the format validation (file-path, directory-path, file-path-pattern) for cloud storage paths, allowing pipelines with default cloud storage paths to be launched without requiring access to those paths during parameter validation. This extends the approach taken in PR nextflow-io#179 for Azure paths to also cover S3 and Google Cloud Storage paths. Fixes nf-core/sarek#2079 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
1 parent dd78f0e commit fd3606b

File tree

7 files changed

+174
-2
lines changed

7 files changed

+174
-2
lines changed

CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
# nextflow-io/nf-schema: Changelog
22

3+
# Version 2.6.2 (Unreleased)
4+
5+
## Bug fixes
6+
7+
1. Fixed a bug where cloud storage paths (S3, Azure, Google Cloud Storage) would fail validation with "could not validate file format" errors when validating `file-path` and `directory-path` format parameters. Cloud storage paths are now skipped during format validation since accessibility cannot be reliably checked without proper credentials.
8+
39
# Version 2.6.1
410

511
This is a quick patch release to bump the Nextflow Gradle plugin to `1.0.0-beta.11`. This will enable support for the configuration options in the Nextflow language server.

src/main/groovy/nextflow/validation/validators/evaluators/FormatDirectoryPathEvaluator.groovy

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,12 @@ import java.nio.file.Path
1616
class FormatDirectoryPathEvaluator implements Evaluator {
1717
// The string should be a directory
1818

19+
private static final List<String> CLOUD_STORAGE_SCHEMES = ['s3://', 'az://', 'gs://']
20+
21+
private static boolean isCloudStoragePath(String value) {
22+
return CLOUD_STORAGE_SCHEMES.any { value.startsWith(it) }
23+
}
24+
1925
@Override
2026
public Evaluator.Result evaluate(EvaluationContext ctx, JsonNode node) {
2127
// To stay consistent with other keywords, types not applicable to this keyword should succeed
@@ -24,6 +30,14 @@ class FormatDirectoryPathEvaluator implements Evaluator {
2430
}
2531

2632
def String value = node.asString()
33+
34+
// Skip validation for cloud storage paths - we cannot reliably check
35+
// accessibility or directory status without proper credentials
36+
if (isCloudStoragePath(value)) {
37+
log.debug("Skipping directory validation for cloud storage path: ${value}")
38+
return Evaluator.Result.success()
39+
}
40+
2741
def Path file
2842

2943
try {
@@ -32,7 +46,7 @@ class FormatDirectoryPathEvaluator implements Evaluator {
3246
file.exists() // Do an exists check to see if the file can be correctly accessed
3347
}
3448
} catch (Exception e) {
35-
return Evaluator.Result.failure("could not validate file format of '${value}': ${e.message}" as String)
49+
return Evaluator.Result.failure("could not validate directory format of '${value}': ${e.message}" as String)
3650
}
3751

3852
// Actual validation logic

src/main/groovy/nextflow/validation/validators/evaluators/FormatFilePathEvaluator.groovy

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,12 @@ import java.nio.file.Path
1616
class FormatFilePathEvaluator implements Evaluator {
1717
// The string should be a file
1818

19+
private static final List<String> CLOUD_STORAGE_SCHEMES = ['s3://', 'az://', 'gs://']
20+
21+
private static boolean isCloudStoragePath(String value) {
22+
return CLOUD_STORAGE_SCHEMES.any { value.startsWith(it) }
23+
}
24+
1925
@Override
2026
public Evaluator.Result evaluate(EvaluationContext ctx, JsonNode node) {
2127
// To stay consistent with other keywords, types not applicable to this keyword should succeed
@@ -24,6 +30,14 @@ class FormatFilePathEvaluator implements Evaluator {
2430
}
2531

2632
def String value = node.asString()
33+
34+
// Skip validation for cloud storage paths - we cannot reliably check
35+
// accessibility or file/directory status without proper credentials
36+
if (isCloudStoragePath(value)) {
37+
log.debug("Skipping file validation for cloud storage path: ${value}")
38+
return Evaluator.Result.success()
39+
}
40+
2741
def Path file
2842

2943
try {

src/main/groovy/nextflow/validation/validators/evaluators/FormatFilePathPatternEvaluator.groovy

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,13 @@ import java.nio.file.Path
1515
@Slf4j
1616
class FormatFilePathPatternEvaluator implements Evaluator {
1717
// The string should be a path pattern
18-
18+
19+
private static final List<String> CLOUD_STORAGE_SCHEMES = ['s3://', 'az://', 'gs://']
20+
21+
private static boolean isCloudStoragePath(String value) {
22+
return CLOUD_STORAGE_SCHEMES.any { value.startsWith(it) }
23+
}
24+
1925
@Override
2026
public Evaluator.Result evaluate(EvaluationContext ctx, JsonNode node) {
2127
// To stay consistent with other keywords, types not applicable to this keyword should succeed
@@ -24,6 +30,14 @@ class FormatFilePathPatternEvaluator implements Evaluator {
2430
}
2531

2632
def String value = node.asString()
33+
34+
// Skip validation for cloud storage paths - we cannot reliably check
35+
// accessibility or file/directory status without proper credentials
36+
if (isCloudStoragePath(value)) {
37+
log.debug("Skipping file path pattern validation for cloud storage path: ${value}")
38+
return Evaluator.Result.success()
39+
}
40+
2741
def List<Path> files
2842

2943
try {

src/main/groovy/nextflow/validation/validators/evaluators/SchemaEvaluator.groovy

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,12 @@ import nextflow.validation.validators.ValidationResult
2323
class SchemaEvaluator implements Evaluator {
2424
// Evaluate the file using the given schema
2525

26+
private static final List<String> CLOUD_STORAGE_SCHEMES = ['s3://', 'az://', 'gs://']
27+
28+
private static boolean isCloudStoragePath(String value) {
29+
return CLOUD_STORAGE_SCHEMES.any { value.startsWith(it) }
30+
}
31+
2632
private final String schema
2733
private final String baseDir
2834
private final ValidationConfig config
@@ -42,6 +48,13 @@ class SchemaEvaluator implements Evaluator {
4248

4349
def String value = node.asString()
4450

51+
// Skip validation for cloud storage paths - we cannot reliably check
52+
// accessibility without proper credentials
53+
if (isCloudStoragePath(value)) {
54+
log.debug("Skipping schema validation for cloud storage path: ${value}")
55+
return Evaluator.Result.success()
56+
}
57+
4558
// Actual validation logic
4659
def Path file = Nextflow.file(value)
4760
// Don't validate if the file does not exist or is a directory

src/test/groovy/nextflow/validation/ValidateParametersTest.groovy

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1397,4 +1397,76 @@ class ValidateParametersTest extends Dsl2Spec{
13971397
noExceptionThrown()
13981398
stdout == ["* --testing: test", "* --genomebutlonger: true"]
13991399
}
1400+
1401+
def 'should validate S3 storage paths without errors' () {
1402+
given:
1403+
def schema = Path.of('src/testResources/nextflow_schema_cloud_path.json').toAbsolutePath().toString()
1404+
def SCRIPT = """
1405+
params.s3_file = 's3://my-bucket/path/to/file.txt'
1406+
params.s3_directory = 's3://my-bucket/path/to/directory/'
1407+
include { validateParameters } from 'plugin/nf-schema'
1408+
1409+
validateParameters(parameters_schema: '$schema')
1410+
"""
1411+
1412+
when:
1413+
def config = [:]
1414+
def result = new MockScriptRunner(config).setScript(SCRIPT).execute()
1415+
def stdout = capture
1416+
.toString()
1417+
.readLines()
1418+
.findResults {it.contains('WARN nextflow.validation.SchemaValidator') || it.startsWith('* --') ? it : null }
1419+
1420+
then:
1421+
noExceptionThrown()
1422+
!stdout
1423+
}
1424+
1425+
def 'should validate Google Cloud Storage paths without errors' () {
1426+
given:
1427+
def schema = Path.of('src/testResources/nextflow_schema_cloud_path.json').toAbsolutePath().toString()
1428+
def SCRIPT = """
1429+
params.gs_file = 'gs://my-bucket/path/to/file.txt'
1430+
params.gs_directory = 'gs://my-bucket/path/to/directory/'
1431+
include { validateParameters } from 'plugin/nf-schema'
1432+
1433+
validateParameters(parameters_schema: '$schema')
1434+
"""
1435+
1436+
when:
1437+
def config = [:]
1438+
def result = new MockScriptRunner(config).setScript(SCRIPT).execute()
1439+
def stdout = capture
1440+
.toString()
1441+
.readLines()
1442+
.findResults {it.contains('WARN nextflow.validation.SchemaValidator') || it.startsWith('* --') ? it : null }
1443+
1444+
then:
1445+
noExceptionThrown()
1446+
!stdout
1447+
}
1448+
1449+
def 'should validate Azure storage paths without errors' () {
1450+
given:
1451+
def schema = Path.of('src/testResources/nextflow_schema_cloud_path.json').toAbsolutePath().toString()
1452+
def SCRIPT = """
1453+
params.az_file = 'az://my-container/path/to/file.txt'
1454+
params.az_directory = 'az://my-container/path/to/directory/'
1455+
include { validateParameters } from 'plugin/nf-schema'
1456+
1457+
validateParameters(parameters_schema: '$schema')
1458+
"""
1459+
1460+
when:
1461+
def config = [:]
1462+
def result = new MockScriptRunner(config).setScript(SCRIPT).execute()
1463+
def stdout = capture
1464+
.toString()
1465+
.readLines()
1466+
.findResults {it.contains('WARN nextflow.validation.SchemaValidator') || it.startsWith('* --') ? it : null }
1467+
1468+
then:
1469+
noExceptionThrown()
1470+
!stdout
1471+
}
14001472
}
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
{
2+
"$schema": "https://json-schema.org/draft/2020-12/schema",
3+
"$id": "https://raw.githubusercontent.com/nf-core/nf-schema/master/examples/cloudpath/nextflow_schema.json",
4+
"title": "Cloud storage path validation test",
5+
"description": "Test schema for cloud storage path validation",
6+
"type": "object",
7+
"properties": {
8+
"s3_file": {
9+
"type": "string",
10+
"format": "file-path",
11+
"description": "S3 storage file path"
12+
},
13+
"s3_directory": {
14+
"type": "string",
15+
"format": "directory-path",
16+
"description": "S3 storage directory path"
17+
},
18+
"gs_file": {
19+
"type": "string",
20+
"format": "file-path",
21+
"description": "Google Cloud Storage file path"
22+
},
23+
"gs_directory": {
24+
"type": "string",
25+
"format": "directory-path",
26+
"description": "Google Cloud Storage directory path"
27+
},
28+
"az_file": {
29+
"type": "string",
30+
"format": "file-path",
31+
"description": "Azure storage file path"
32+
},
33+
"az_directory": {
34+
"type": "string",
35+
"format": "directory-path",
36+
"description": "Azure storage directory path"
37+
}
38+
}
39+
}

0 commit comments

Comments
 (0)